[
  {
    "path": ".gitignore",
    "content": "**/__pycache__/**\n**/wandb/**\n**/.vscode/**\n*.out\n*.err\n*.zip\n*.tar\n*.pth\nstdout_train.txt\nstdout_eval.txt\nstdout_visualize.txt\nhostfile\n**/olds/**\n**/old/**\n*.pt\n*.txt\nmiccai_generate_Dir\ngenerate.py\nvisualzie.py"
  },
  {
    "path": "README.md",
    "content": "\n## LGRNet: Local-Global Reciprocal Network for Video Polyp Segmentation [`Paper`](https://arxiv.org/abs/2407.05703) | [`BibTeX`](#citing) |  [`Huggingface(UFUV Dataset)`](https://huggingface.co/datasets/huihuixu/uterine_fibroid_ultrasound_video_segmentation)\n\nHuihui Xu, Yijun Yang, Angelica Aviles-Rivero, Guang Yang, Jing Qin, and Lei Zhu\n\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/lgrnet-local-global-reciprocal-network-for/video-polyp-segmentation-on-sun-seg-hard)](https://paperswithcode.com/sota/video-polyp-segmentation-on-sun-seg-hard?p=lgrnet-local-global-reciprocal-network-for)\n\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/lgrnet-local-global-reciprocal-network-for/video-polyp-segmentation-on-sun-seg-easy)](https://paperswithcode.com/sota/video-polyp-segmentation-on-sun-seg-easy?p=lgrnet-local-global-reciprocal-network-for)\n\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/lgrnet-local-global-reciprocal-network-for/video-polyp-segmentation-on-sun-seg-easy-1)](https://paperswithcode.com/sota/video-polyp-segmentation-on-sun-seg-easy-1?p=lgrnet-local-global-reciprocal-network-for)\n\t\n[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/lgrnet-local-global-reciprocal-network-for/video-polyp-segmentation-on-sun-seg-hard-1)](https://paperswithcode.com/sota/video-polyp-segmentation-on-sun-seg-hard-1?p=lgrnet-local-global-reciprocal-network-for)\n\n\nThis is the official implmentation of LGRNet (MICCAI'24 Early Accept), which incorporates local **[Cyclic Neighborhoold Propagation](https://github.com/bio-mlhui/LGRNet/blob/main/models/encoder/neighborhood_qk.py#L57)** and global **[Hilbert Selective Scan](https://github.com/bio-mlhui/LGRNet/blob/main/models/encoder/ops/modules/frame_query_ss2d.py#L531)**. Together with the notion of **[Frame Bottleneck Queries](https://github.com/bio-mlhui/LGRNet/blob/main/models/encoder/localGlobal.py#L185)**, LGRNet can both efficiently and effectively aggregate the local-global temporal context, which achieves *state-of-the-art* on the public [Video Polyp Segmentation(VPS)](https://paperswithcode.com/task/video-polyp-segmentation) benchmark.\n\n<div align=\"justify\">As an example for ultrasound video, a single frame is too noisy and insufficient for accurate lesion diagnosis. In practice, doctors need to check neighboring frames(local) and collect all visual clues (global) in the video to predict possible lesion region and filter out irrelevent surrounding issues. </div>\n</br>\n<div align=\"center\" style=\"padding: 0 100pt\">\n<img src=\"assets/images/pipeline.png\">\n</div>\n</br>\n<div align=\"justify\"> In CNP, each token takes the neighborhood tokens (defined by a kernel) in the cyclic frame as attention keys. CNP enables aggregating the local(cyclic) temporal information into one token. In Hilbert Selective Scan, a set of frame bottleneck queries are used to aggreate spatial information from each frame. Then, we use Hilbert Selective Scan to efficiently parse the global temporal context based on these bottleneck queries. The global temporal context is then propagated back to the feature maps by a Distribute layer. Based on Mask2Former, the decoder can output a set of different mask predictions with corresponding confidence score, which also facilitates comprehesive diagnosis.</div>\n\n\n## Items\n\n1. Installation: Please refer to [INSTALL.md](assets/INSTALL.md) for more details.\n2. Data preparation: Please refer to [DATA.md](assets/DATA.md) for more details.\n\n\n3. Training: \n\nChange PORT_NUM for DDP and make sure the $CURRENT_TASK is 'VIS':\n```\nexport CURRENT_TASK=VIS\nexport MASTER_ADDR=127.0.0.1\nexport MASTER_PORT=PORT_NUM\n```\n\nMake sure the $PT_PATH and $DATASET_PATH are correctly set during installation and preparing data.\n\nThe training on SUN-SEG is conducted using 2 4090-24GB GPUs:\n```\nCUDA_VISIBLE_DEVICES=0,1 TORCH_NUM_WORKERS=8 python main.py --config_file output/VIS/sunseg/pvt/pvt.py --trainer_mode train_attmpt\n```\n\n4. logs, checkpoints, predictions\n\n| Backbone| Dataset | Dice | mIou  | log | ckpt | predictions |\n| :----: | :----: | :----: | :----: | :----: | :----: |:----: |\n| PVTv2-B2 | SUN-SEG-Train | -- | -- | [log](https://drive.google.com/file/d/17MTOYW73RLbvZS3BLFBZEphY_0JzN6er/view?usp=sharing) | [ckpt](https://drive.google.com/file/d/1D4YAIfFCCQIsDfKgSCr9tCw7vDAgqf76/view?usp=sharing) | --\n| PVTv2-B2 | SUN-SEG-Hard-Testing | 0.876 | 0.805 | [log](https://drive.google.com/file/d/1wdVMWMknSlURaBROWbMax4iS9V1Tbn9-/view?usp=sharing) |[ckpt](https://drive.google.com/file/d/1D4YAIfFCCQIsDfKgSCr9tCw7vDAgqf76/view?usp=sharing) | [mask predictions](https://drive.google.com/file/d/1V8CDMC87o7t4eyts4BVEwflDUrFpAOVX/view?usp=sharing)\n| PVTv2-B2 | SUN-SEG-Easy-Testing | 0.875 | 0.810 | [log](https://drive.google.com/file/d/1wdVMWMknSlURaBROWbMax4iS9V1Tbn9-/view?usp=sharing) |[ckpt](https://drive.google.com/file/d/1D4YAIfFCCQIsDfKgSCr9tCw7vDAgqf76/view?usp=sharing) | [mask predictions](https://drive.google.com/file/d/1V8CDMC87o7t4eyts4BVEwflDUrFpAOVX/view?usp=sharing)\n| PVTv2-B2 | SUN-SEG-Hard-Unseen-Testing | 0.865 | 0.792 | [log](https://drive.google.com/file/d/1obt_qvWCvslhRY-e4SrTJNS0r6Diad4e/view?usp=sharing) | [ckpt](https://drive.google.com/file/d/1D4YAIfFCCQIsDfKgSCr9tCw7vDAgqf76/view?usp=sharing) | [mask predictions](https://drive.google.com/file/d/1V8CDMC87o7t4eyts4BVEwflDUrFpAOVX/view?usp=sharing)\n| PVTv2-B2 | SUN-SEG-Easy-Unseen-Testing | 0.853 | 0.783 | [log](https://drive.google.com/file/d/1obt_qvWCvslhRY-e4SrTJNS0r6Diad4e/view?usp=sharing) | [ckpt](https://drive.google.com/file/d/1D4YAIfFCCQIsDfKgSCr9tCw7vDAgqf76/view?usp=sharing)| [mask predictions](https://drive.google.com/file/d/1V8CDMC87o7t4eyts4BVEwflDUrFpAOVX/view?usp=sharing)\n| Res2Net-50 | SUN-SEG-Hard-Testing | 0.841 | 0.765 | [log](https://drive.google.com/file/d/17pUxFMuHpPD_In5RVrJUsPFZGOgNFzb6/view?usp=sharing) |\n| Res2Net-50 | SUN-SEG-Easy-Testing | 0.843 | 0.774 | [log](https://drive.google.com/file/d/17pUxFMuHpPD_In5RVrJUsPFZGOgNFzb6/view?usp=sharing) |\n| PVTv2-B2 | CVC612V | 0.933 | 0.877 | [log](https://drive.google.com/file/d/1m36mJL0Fu3T9F73TqFGnFsWaCGh3JDeJ/view?usp=drive_link) |\n| PVTv2-B2 | CVC300TV | 0.916 | 0.852 | [log](https://drive.google.com/file/d/1m36mJL0Fu3T9F73TqFGnFsWaCGh3JDeJ/view?usp=drive_link) |\n| PVTv2-B2 | CVC612T | 0.875 | 0.814 | [log](https://drive.google.com/file/d/1m36mJL0Fu3T9F73TqFGnFsWaCGh3JDeJ/view?usp=drive_link) |\n\n\n5. Evaluate:\nEvaluating on SUN-SEG-Easy AND SUN-SEG-Hard using 1 4090-24GPU GPUS (**modify the ckpt_path to the absolute path**):\n```\nCUDA_VISIBLE_DEVICES=0 TORCH_NUM_WORKERS=8 python main.py --config_file output/VIS/sunseg/pvt/pvt.py --trainer_mode eval --eval_path ckpt_path\n```\n\n## citing\n```\n@article{xu2024lgrnet,\n  title={LGRNet: Local-Global Reciprocal Network for Uterine Fibroid Segmentation in Ultrasound Videos},\n  author={Xu, Huihui and Yang, Yijun and Aviles-Rivero, Angelica I and Yang, Guang and Qin, Jing and Zhu, Lei},\n  journal={arXiv preprint arXiv:2407.05703},\n  year={2024}\n}\n``` \n\n## Acknowledgments\n- Thanks [Gilbert](https://github.com/jakubcerveny/gilbert) for the implementation of Hilbert curve generation.\n- Thanks GPT4 for helping me constructing idea of Hilbert Filling Curve v.s. Zigzag curve"
  },
  {
    "path": "assets/DATA.md",
    "content": "\n\n\n\n\n\n# Data Preparation\n\n## UFUV (Private): \nplease email the second author for UFUV dataset if you want, I have no absolute power for UFUV\n\n## VPS (Public)\n\n### CVC/Kvasir/Mayo\nWe follow [PNS-Net](https://github.com/GewelsJI/PNS-Net) to download the CVC/Kvasir/Mayo dataset. The download link is same as [link](https://drive.google.com/file/d/1TyaRy4c4nHFDa3o2bOl4dP5Z7wes7HV2/view?usp=sharing)\n\nPut MICCAI-VPS-dataset.zip in $DATASET_PATH, then run following script to change the directory structure:\n```\ncd $DATASET_PATH\nunzip -qq MICCAI-VPS-dataset.zip\n\n# cd LGRNet directory\n# normalize the VPS data structure\npython handle_vps.py\n```\n\nNow the structure should be like:\n```\n${DATASET_PATH}\n    -- MICCAI-VPS-dataset\n        -- Kvasir-SEG\n            -- *\n        -- VPS-TestSet\n            -- CVC-ColonDB-300\n                -- *\n            -- CVC-ClinicDB-612-Valid\n                -- *\t\t\n            -- CVC-ClinicDB-612-Test\n                -- *\n        -- VPS-TrainSet\n            -- ASU-Mayo_Clinic\n                -- Train\n                    -- *\n            -- CVC-ClinicDB-612\t\n                -- Train\n                    -- *\n            -- CVC-ColonDB-300\n                -- Train\n                    -- *\n```\nwhere * means the following structure:\n```\n-- Frame\n    -- vid1\n        -- img file\n-- GT\n    -- vid1\n        -- mask file\n```\n\n### SUN-SEG\n\nPlease follow https://github.com/GewelsJI/VPS/blob/main/docs/DATA_PREPARATION.md to email the author for SUN-SEG.\nPut part1, part2, annotation in $DATASET_PATH/SUN-SEG\n\n```\n# normalize the directory\nunzip -qq $DATASET_PATH/SUN-SEG/sundatabase_positive_part1.zip -d $DATASET_PATH/SUN-SEG/SUN-Positive\nunzip -qq $DATASET_PATH/SUN-SEG/sundatabase_positive_part2.zip -d $DATASET_PATH/SUN-SEG/SUN-Positive\ntar -xf $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation.tar -C $DATASET_PATH/SUN-SEG/\n\nrm -rf $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestEasyDataset/Unseen/Frame\nfind $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation -name \"._.DS_Store\" -type f -delete\npython reorganize_sunseg.py\n\n```\nNow the structure should be like:\n```\n${DATASET_PATH}\n    -- SUN-SEG\n        -- SUN-SEG-Annotation\n            -- TrainDataset\n                -- *\n            -- TestEasyDataset\n                -- combine\n                    -- *\n            -- TestHardDataset\n                -- combine\n                    -- *\n```\n\n\n\n"
  },
  {
    "path": "assets/INSTALL.md",
    "content": "# Install\n## Requirements\nWe test the codes in the following environments\n\n- CUDA 12.1\n- Python 3.10.13\n- Pytorch 2.1.1\n- Torchvison 0.16.1\n- detectron 0.6\n- mamba_ssm 1.2.0.post1\n- natten 0.15.1\n- timm 0.9.12\n\n## Install environment for LGRNet\n\n```\nconda create --name lgrnet python=3.10\nconda activate lgrnet\n\n# make sure CUDA-12.1 is installed and activated in env var.\n\n# install torch\npip install torch==2.1.1 torchvision==0.16.1 torchaudio==2.1.1 --index-url https://download.pytorch.org/whl/cu121\n\n\n# install detectron2, building may take much time.\npython -m pip install 'git+https://github.com/facebookresearch/detectron2.git'\n\n# install mamba, see https://github.com/state-spaces/mamba\ncd .. \ngit clone https://github.com/state-spaces/mamba.git\ncd mamba\npip install . --no-build-isolation\ncd ../LGRNet\n\n# install natten, see https://github.com/SHI-Labs/NATTEN/blob/main/docs/install.md\npip install natten==0.15.1+torch210cu121 -f https://shi-labs.com/natten/wheels/cu121/torch2.1.0/natten-0.15.1%2Btorch210cu121-cp310-cp310-linux_x86_64.whl\n\n# misc\npip install albumentations==1.3.1\npip install Pygments\npip install imgaug\npip install timm==0.9.12\n\n# compile deform attention\ncd models/encoder/ops/\npython setup.py build install --user\n\n# download resnet/pvtv2 ckpt, our model uses the same backbone with WeakPoly(phttps://github.com/weijun88/WeakPolyp)\nwget -P $PT_PATH/pvt_v2/pvt_v2_b2.pth  https://huggingface.co/huihuixu/lgrnet_ckpts/blob/main/pvt_v2_b2.pth  \nwget -P $PT_PATH/res2net/res2net50_v1b_26w_4s-3cf99910.pth  https://huggingface.co/huihuixu/lgrnet_ckpts/blob/main/res2net50_v1b_26w_4s-3cf99910.pth\n\n```\n"
  },
  {
    "path": "assets/MODEL_ZOO.md",
    "content": ""
  },
  {
    "path": "data_schedule/__init__.py",
    "content": "\nimport os\n\nif os.getenv('CURRENT_TASK') == 'VIS':\n    from . import vis\nelse:\n    raise ValueError()\n\ndef build_schedule(configs, model_input_mapper, model_input_collate_fn):\n    import logging\n    from functools import partial\n    import detectron2.utils.comm as comm\n    from torch.utils.data import DataLoader, ConcatDataset\n    from .registry import MAPPER_REGISTRY, EVALUATOR_REGISTRY\n    from detectron2.data import DatasetCatalog, DatasetFromList, MapDataset, MetadataCatalog\n    from data_schedule.utils.sampler import Evaluate_ExactSampler_Distributed, Train_InfiniteSampler_Distributed\n    datasets = {'train': [], 'evaluate': []}\n    meta_idx_shift = 0 \n    for mode in ['train', 'evaluate']:\n        for dataset_name in configs['data'][mode].keys():\n            dataset_assume_mode = MetadataCatalog.get(dataset_name).get('mode')\n            if dataset_assume_mode != mode:\n                logging.warning(f'default mode of {dataset_name} is {dataset_assume_mode} not {mode}')\n            dataset_dicts = DatasetFromList(DatasetCatalog.get(dataset_name), copy=True, serialize=True)\n            mapper = MAPPER_REGISTRY.get(configs['data'][mode][dataset_name]['mapper']['name'])(mode=mode,\n                                                                                                dataset_name=dataset_name, \n                                                                                                configs=configs,\n                                                                                                meta_idx_shift=meta_idx_shift if mode == 'train' else 0)\n            meta_idx_shift += len(dataset_dicts)\n            dataset = MapDataset(dataset_dicts, partial(composition, mappers=[mapper, \n                                                                              partial(model_input_mapper, mode=mode)]))\n            if mode == 'train':\n                datasets[mode].append(dataset)\n            else:\n                datasets[mode].append((dataset_name, dataset))\n\n    train_dataset = ConcatDataset(datasets['train'])\n    logging.debug(f'Total number of training meta: {len(train_dataset)}')\n\n    train_loader_splits = configs['optim']['splits']\n    batch_sizes = configs['optim']['batch_sizes']\n    splits = list(zip(train_loader_splits[:-1], train_loader_splits[1:]))\n    assert len(splits) == (len(batch_sizes))\n    inf_stream_fn = partial(infinite_indices,\n                            seed=configs['stream_idx_seed'], \n                            batch_sizes=configs['optim']['batch_sizes'],\n                            splits=configs['optim']['splits'],\n                            one_batch_two_epoch=configs['optim']['one_batch_two_epoch'],\n                            dataset_length=len(train_dataset),\n                            shuffle=True) \n    train_samplers = []\n    train_loaders = []\n    for btch_size, (range_start, range_end) in zip(batch_sizes, splits):\n        if range_end is not None:\n            assert (range_end - range_start) % btch_size == 0, ''\n        assert btch_size % comm.get_world_size() == 0, ''\n        each_process_batch_size = int(btch_size / comm.get_world_size())\n        loader_sampler = Train_InfiniteSampler_Distributed(inf_stream_fn=inf_stream_fn,\n                                                           start_idx=range_start,\n                                                           end_idx=range_end,)\n        train_samplers.append(loader_sampler)\n        train_loaders.append(DataLoader(train_dataset,\n                                        batch_size=each_process_batch_size,\n                                        sampler=loader_sampler,\n                                        collate_fn=partial(model_input_collate_fn, mode='train'), \n                                        num_workers=int(os.getenv('TORCH_NUM_WORKERS')),\n                                        pin_memory=True,\n                                        persistent_workers=True))\n\n    evaluators = []\n    for eval_dataset_name, eval_dataset in datasets['evaluate']:\n        logging.debug(f'Number of evaluate meta in {eval_dataset_name}: {len(eval_dataset)}')\n        loader = DataLoader(eval_dataset, \n                            batch_size=1, \n                            sampler=Evaluate_ExactSampler_Distributed(eval_dataset),\n                            collate_fn=partial(model_input_collate_fn, mode='evaluate'),\n                            num_workers=int(os.getenv('TORCH_NUM_WORKERS')),\n                            pin_memory=True,\n                            persistent_workers=True)\n        \n        evaluator = EVALUATOR_REGISTRY.get(configs['data']['evaluate'][eval_dataset_name]['evaluator']['name'])(configs=configs,\n                                                                                                                dataset_name=eval_dataset_name,\n                                                                                                                data_loader=loader)\n        evaluators.append((eval_dataset_name, evaluator))\n\n    return train_samplers, train_loaders, partial(evaluate_call, evaluators=evaluators)\n\ndef composition(data_dict, mappers):\n    for mappper in mappers:\n        data_dict = mappper(data_dict)\n        if data_dict is None:\n            return None\n    return data_dict\n\ndef evaluate_call(evaluators, model, output_dir):\n    import detectron2.utils.comm as comm\n    ret = {}\n    for eval_dataset_name, evaluator in evaluators:\n        metric_dict = evaluator(model=model,output_dir=output_dir)\n        if comm.is_main_process():\n            for key, value in metric_dict.items():\n                assert f'{key}_{eval_dataset_name}' not in ret\n                ret[f'{key}_{eval_dataset_name}'] = value\n        comm.synchronize()\n    return ret\n\n\ndef _infinite_indices(seed, dataset_length, shuffle=True,):\n    import torch\n    g = torch.Generator()\n    g.manual_seed(seed)\n    while True:\n        if shuffle:\n            yield from torch.randperm(dataset_length, generator=g).tolist()\n        else:\n            yield from torch.arange(dataset_length).tolist()\n\ndef infinite_indices(seed, \n                     dataset_length, \n                     batch_sizes, \n                     splits, \n                     one_batch_two_epoch='just_use',\n                     shuffle=True): # 'abandon', 'just_use', 'pad'\n    import torch\n    import math\n    g = torch.Generator()\n    g.manual_seed(seed)\n\n    split_ranges = list(zip(splits[:-1], splits[1:]))\n    assert len(split_ranges) == (len(batch_sizes))\n    stream = _infinite_indices(seed, dataset_length=dataset_length, shuffle=shuffle)\n\n    stream_throw_cnt = 0\n    cnt = 0\n    for (range_start, range_end), btch_size in zip(split_ranges, batch_sizes):\n        assert cnt == range_start\n        if range_end == None:\n            range_end = math.inf\n        \n        while cnt < range_end:\n            epoch_milestone = ((stream_throw_cnt // dataset_length) + 1 ) * dataset_length\n            if (stream_throw_cnt < epoch_milestone) and (stream_throw_cnt + btch_size > epoch_milestone) and (one_batch_two_epoch != 'just_use'):\n                if one_batch_two_epoch == 'abandon':\n                    for _ in range(epoch_milestone - stream_throw_cnt):\n                        abandon = next(stream)\n                        stream_throw_cnt += 1\n\n                elif one_batch_two_epoch == 'pad':\n                    diff = stream_throw_cnt + btch_size - epoch_milestone\n                    num_throw = btch_size - diff\n                    rand_idxs = torch.randperm(dataset_length, generator=g)[:diff].tolist()\n                    for _ in range(num_throw):\n                        cnt += 1\n                        stream_throw_cnt += 1\n                        yield next(stream)\n                    for idx in rand_idxs:\n                        cnt += 1\n                        yield idx\n                else:\n                    raise ValueError()\n            else:\n                for _ in range(btch_size):\n                    cnt += 1\n                    stream_throw_cnt += 1\n                    yield next(stream)  \n \n        assert cnt == range_end\n"
  },
  {
    "path": "data_schedule/registry.py",
    "content": "from detectron2.utils.registry import Registry\n\n\nEVALUATOR_REGISTRY = Registry('EVALUATOR')\nMAPPER_REGISTRY = Registry('MAPPER')\n\nclass Mapper:\n    def __init__(self, \n                meta_idx_shift,\n                dataset_meta,) -> None:\n        self.meta_idx_shift = meta_idx_shift \n        self.visualized_meta_idxs = dataset_meta.get('visualize_meta_idxs') \n\n    def _call(self, data_dict):\n        pass\n\n    def __call__(self, data_dict):\n        meta_idx = data_dict['meta_idx']\n        ret = self._call(data_dict)\n        if ret is None:\n            return None\n        ret['meta_idx'] = meta_idx + self.meta_idx_shift\n        if meta_idx in self.visualized_meta_idxs:\n            ret['visualize'] = True\n        else:\n            ret['visualize'] = False\n        return ret\n    \n\n\n\n\n\n"
  },
  {
    "path": "data_schedule/utils/box_ops.py",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n\"\"\"\nUtilities for bounding box manipulation and GIoU.\n\"\"\"\nimport torch\nfrom torchvision.ops.boxes import box_area\n\n\ndef box_cxcywh_to_xyxy(x):\n    x_c, y_c, w, h = x.unbind(-1)\n    b = [(x_c - 0.5 * w), (y_c - 0.5 * h),\n         (x_c + 0.5 * w), (y_c + 0.5 * h)]\n    return torch.stack(b, dim=-1)\n\n\ndef box_xyxy_to_cxcywh(x):\n    x0, y0, x1, y1 = x.unbind(-1)\n    assert ((x1 - x0) >= 0).all()\n    assert ((y1 - y0) >= 0).all()\n    b = [(x0 + x1) / 2, (y0 + y1) / 2,\n         (x1 - x0), (y1 - y0)]\n    return torch.stack(b, dim=-1)\n\n\n# modified from torchvision to also return the union\ndef box_iou(boxes1, boxes2):\n    area1 = box_area(boxes1)\n    area2 = box_area(boxes2)\n\n    lt = torch.max(boxes1[:, None, :2], boxes2[:, :2])  # [N,M,2]\n    rb = torch.min(boxes1[:, None, 2:], boxes2[:, 2:])  # [N,M,2]\n\n    wh = (rb - lt).clamp(min=0)  # [N,M,2]\n    inter = wh[:, :, 0] * wh[:, :, 1]  # [N,M]\n\n    union = area1[:, None] + area2 - inter\n\n    iou = inter / union\n    return iou, union\n\n\ndef generalized_box_iou(boxes1, boxes2):\n    \"\"\"\n    Generalized IoU from https://giou.stanford.edu/\n\n    The boxes should be in [x0, y0, x1, y1] format\n\n    Returns a [N, M] pairwise matrix, where N = len(boxes1)\n    and M = len(boxes2)\n    \"\"\"\n    # degenerate boxes gives inf / nan results\n    # so do an early check\n    assert (boxes1[:, 2:] >= boxes1[:, :2]).all()\n    assert (boxes2[:, 2:] >= boxes2[:, :2]).all()\n    iou, union = box_iou(boxes1, boxes2)\n\n    lt = torch.min(boxes1[:, None, :2], boxes2[:, :2])\n    rb = torch.max(boxes1[:, None, 2:], boxes2[:, 2:])\n\n    wh = (rb - lt).clamp(min=0)  # [N,M,2]\n    area = wh[:, :, 0] * wh[:, :, 1]\n\n    return iou - (area - union) / area\n\n\ndef masks_to_boxes(masks):\n    \"\"\"Compute the bounding boxes around the provided masks\n\n    The masks should be in format [N, H, W] where N is the number of masks, (H, W) are the spatial dimensions.\n\n    Returns a [N, 4] tensors, with the boxes in xyxy format\n    \"\"\"\n    if masks.numel() == 0:\n        return torch.zeros((0, 4), device=masks.device)\n\n    h, w = masks.shape[-2:]\n\n    y = torch.arange(0, h, dtype=torch.float)\n    x = torch.arange(0, w, dtype=torch.float)\n    y, x = torch.meshgrid(y, x)\n\n    x_mask = (masks * x.unsqueeze(0))\n    x_max = x_mask.flatten(1).max(-1)[0]\n    x_min = x_mask.masked_fill(~(masks.bool()), 1e8).flatten(1).min(-1)[0]\n\n    y_mask = (masks * y.unsqueeze(0))\n    y_max = y_mask.flatten(1).max(-1)[0]\n    y_min = y_mask.masked_fill(~(masks.bool()), 1e8).flatten(1).min(-1)[0]\n\n    return torch.stack([x_min, y_min, x_max, y_max], 1)\n"
  },
  {
    "path": "data_schedule/utils/sampler.py",
    "content": "\nimport math\nimport torch.distributed as dist\nfrom typing import TypeVar, Optional, Iterator        \nT_co = TypeVar('T_co', covariant=True)\nfrom torch.utils.data.distributed import DistributedSampler\nimport detectron2.utils.comm as comm\nfrom utils.misc import all_gather\nfrom torch.utils.data import Sampler\nimport torch\n\nimport logging\nimport itertools\n\nclass TrainRandomSampler_ByEpoch(Sampler[int]):\n    def __init__(self, \n                 data_source,\n                 seed,\n                 ) -> None:\n        self.data_source = data_source\n        self.num_samples = len(self.data_source)\n        self.seed = seed\n        self.epoch = None\n\n    def __iter__(self):\n        seed = self.seed + self.epoch\n        print(f'generating a new indices permutations for this epoch using seed {seed}')\n        n = len(self.data_source)\n        g = torch.Generator()\n        g.manual_seed(seed)\n        \n        for _ in range(self.num_samples // n):\n            yield from torch.randperm(n, generator=g).tolist()\n        yield from torch.randperm(n, generator=g).tolist()[:self.num_samples % n]\n\n    def __len__(self) -> int:\n        return self.num_samples\n\n    def set_epoch(self, epoch: int) -> None:\n        r\"\"\"\n\n        Args:\n            epoch (int): Epoch number.\n        \"\"\"\n        self.epoch = epoch\n  \n  \n\nclass Train_InfiniteSampler_Distributed(Sampler[T_co]):\n    def __init__(self, \n                 inf_stream_fn, \n                 start_idx: int = 0,\n                 end_idx = None,\n                 \n                ): \n        self.rank = comm.get_rank()\n        self.num_replicas = comm.get_world_size()\n        self.start_idx = start_idx \n        self.end_idx = end_idx\n        self.inf_stream_fn = inf_stream_fn\n\n    def set_iter_first_sample_idx(self, idx):\n        self.start_idx = idx\n\n    def set_iter_last_sample_idx(self, idx):\n        self.end_idx = idx\n\n    def __iter__(self) -> Iterator[T_co]:\n        logging.debug(f'在 infinite stream 上定位到{self.start_idx} 为开头')\n        yield from itertools.islice(self.inf_stream_fn(), self.start_idx + self.rank, self.end_idx, self.num_replicas)\n\nclass Evaluate_ExactSampler_Distributed(Sampler[T_co]):\n    def __init__(self, dataset) -> None:\n        self.dataset = dataset\n        self.rank = comm.get_rank()\n        self.num_replicas = comm.get_world_size()\n        indices = list(range(len(self.dataset)))  \n        self.indices = indices[self.rank:len(self.dataset):self.num_replicas]\n\n\n    def __iter__(self):\n        yield from self.indices\n\n    def __len__(self):\n        return len(self.indices)\n\n\nclass TrainRandomSampler_ByEpoch_Distributed(Sampler[T_co]):\n    def __init__(self, \n                 dataset, num_replicas,\n                 rank,\n                 seed: int = 0) -> None:\n        if rank >= num_replicas or rank < 0:\n            raise ValueError(\"Invalid rank {}, rank should be in the interval\"\" [0, {}]\".format(rank, num_replicas - 1))\n        self.dataset = dataset\n        self.num_replicas = num_replicas\n        self.rank = rank\n        self.epoch = None\n\n        self.num_samples = math.ceil(len(self.dataset) / self.num_replicas)  \n        self.total_size = self.num_samples * self.num_replicas\n        self.seed = seed\n\n    def __iter__(self) -> Iterator[T_co]:\n        seed = self.seed + self.epoch\n        logging.debug(f'generating a new indices permutations for this epoch using seed {seed}')\n        g = torch.Generator()\n        g.manual_seed(self.seed + self.epoch)\n        indices = torch.randperm(len(self.dataset), generator=g).tolist()\n\n        # add extra samples to make it evenly divisible\n        padding_size = self.total_size - len(indices)\n        if padding_size <= len(indices):\n            indices += indices[:padding_size]\n        else:\n            indices += (indices * math.ceil(padding_size / len(indices)))[:padding_size]\n        assert len(indices) == self.total_size\n        # subsample\n        indices = indices[self.rank:self.total_size:self.num_replicas]\n        assert len(indices) == self.num_samples\n        self.epoch = None \n        return iter(indices)\n\n    def __len__(self) -> int:\n        return self.num_samples\n\n    def set_epoch(self, epoch: int) -> None:\n        r\"\"\"\n        Sets the epoch for this sampler. When :attr:`shuffle=True`, this ensures all replicas\n        use a different random ordering for each epoch. Otherwise, the next iteration of this\n        sampler will yield the same ordering.\n\n        Args:\n            epoch (int): Epoch number.\n        \"\"\"\n        self.epoch = epoch\n\n\nclass InferenceSampler(Sampler):\n    \"\"\"\n    Produce indices for inference across all workers.\n    Inference needs to run on the __exact__ set of samples,\n    therefore when the total number of samples is not divisible by the number of workers,\n    this sampler produces different number of samples on different workers.\n    \"\"\"\n\n    def __init__(self, size: int):\n        \"\"\"\n        Args:\n            size (int): the total number of data of the underlying dataset to sample from\n        \"\"\"\n        self._size = size\n        assert size > 0\n        self._rank = comm.get_rank()\n        self._world_size = comm.get_world_size()\n        self._local_indices = self._get_local_indices(size, self._world_size, self._rank)\n\n    @staticmethod\n    def _get_local_indices(total_size, world_size, rank):\n        shard_size = total_size // world_size\n        left = total_size % world_size\n        shard_sizes = [shard_size + int(r < left) for r in range(world_size)]\n\n        begin = sum(shard_sizes[:rank])\n        end = min(sum(shard_sizes[: rank + 1]), total_size)\n        return range(begin, end)\n\n    def __iter__(self):\n        yield from self._local_indices\n\n    def __len__(self):\n        return len(self._local_indices)"
  },
  {
    "path": "data_schedule/utils/segmentation.py",
    "content": "\nimport torch\n\ndef bounding_box_from_mask(mask):\n    if not mask.any():\n        return torch.zeros([4]).float()\n    rows = torch.any(mask, dim=1) # h\n    cols = torch.any(mask, dim=0) # w\n    row_indexs = torch.where(rows)[0]\n    rmin, rmax = row_indexs.min(), row_indexs.max()\n\n    col_indexs = torch.where(cols)[0]\n    cmin, cmax = col_indexs.min(), col_indexs.max()\n    return torch.tensor([cmin, rmin, cmax, rmax]).float() # x1y1x2y2\n"
  },
  {
    "path": "data_schedule/vis/__init__.py",
    "content": "from . import polyp\nfrom . import mapper \nfrom . import evaluator_fast\nfrom . import vis_aug_eval \nfrom . import vis_aug_train\nfrom . import vis_frame_sampler\n\n\n\n\n"
  },
  {
    "path": "data_schedule/vis/apis.py",
    "content": "\nclass VIS_Dataset:\n    \"\"\"\n    \"\"\"\n\nclass VIS_Aug_CallbackAPI:\n    \"\"\"\n    \"\"\"\n\nclass VIS_Evaluator_OutAPI_EvalFn_API:\n    \"\"\"\n    \"\"\"\n\nclass VIS_TrainAPI_clipped_video:\n    \"\"\"\n    \"\"\"\n\nclass VIS_EvalAPI_clipped_video_request_ann:\n    \"\"\"\n    \"\"\"\n\nclass VIS_FrameSampler_InputOutput_API:\n    \"\"\"\n    \"\"\"\n\nclass GetFrames:\n    \"\"\"\n    \"\"\""
  },
  {
    "path": "data_schedule/vis/evaluator_fast.py",
    "content": "\nimport os\nfrom tqdm import tqdm\nfrom functools import partial\nimport torch\nimport detectron2.utils.comm as comm\nfrom utils.misc import to_device\nfrom detectron2.data import  MetadataCatalog\nfrom data_schedule.registry import EVALUATOR_REGISTRY\n\nfrom .evaluator_utils import vis_metric_entrypoint\nfrom data_schedule.vis.apis import VIS_EvalAPI_clipped_video_request_ann, VIS_Aug_CallbackAPI,  VIS_Evaluator_OutAPI_EvalFn_API\nfrom collections import defaultdict\n\n\n@EVALUATOR_REGISTRY.register()\nclass VIS_Evaluator_FrameFast:\n    def __init__(self,\n                 dataset_name,\n                 data_loader,\n                 configs) -> None:\n        self.dataset_name = dataset_name\n        self.loader = data_loader\n        frame_metrics = configs['data']['evaluate'][dataset_name]['evaluator']['frame_metrics']\n        dataset_meta = MetadataCatalog.get(dataset_name)\n        self.frame_metric_fns = []\n        for metric_name, metric_config in frame_metrics:\n            metric_fn = vis_metric_entrypoint(metric_name)\n            metric_fn = partial(metric_fn, dataset_meta=dataset_meta, **metric_config)\n            self.frame_metric_fns.append(metric_fn)\n            \n        self.eval_meta_keys = dataset_meta.get('eval_meta_keys') \n\n        metrics_aggregator = configs['data']['evaluate'][dataset_name]['evaluator']['metrics_aggregator']\n        self.eval_meta_keys = dataset_meta.get('eval_meta_keys')  # { video_id: list[fnames] }\n        self.metrics_aggregator = partial(vis_metric_entrypoint(metrics_aggregator[0]),\n                                                dataset_meta=dataset_meta,\n                                                eval_meta_keys=self.eval_meta_keys,\n                                                **metrics_aggregator[1])\n\n    def visualize_path(self, meta_idxs, visualize, evaluator_path):\n        return [os.path.join(evaluator_path, f'meta_{meta_idx}') if vis else None for (meta_idx, vis) in zip(meta_idxs, visualize)]\n    \n    @torch.no_grad()\n    def __call__(self, model, output_dir):\n        evaluator_path = os.path.join(output_dir, f'eval_{self.dataset_name}')\n        os.makedirs(evaluator_path, exist_ok=True)\n        macs, params = None, None\n        metrics_by_video_id_frame = defaultdict(dict) \n        for batch_dict in tqdm(self.loader):\n            VIS_EvalAPI_clipped_video_request_ann\n            eval_metas = batch_dict.pop('metas')\n            request_anns = eval_metas['request_ann'][0] # t, bool tensor\n            frame_strs = eval_metas['frames'][0] # t', list[str]\n            video_id = eval_metas['video_id'][0] # str\n            assert request_anns.int().sum() == len(frame_strs)\n            callback_fns = eval_metas['callback_fns'][0] # list[fn]\n            visualize_path = self.visualize_path(meta_idxs=batch_dict['meta_idxs'], visualize=batch_dict['visualize'], \n                                                 evaluator_path=os.path.join(evaluator_path, 'visualize_model'))\n            batch_dict['visualize_paths'] = visualize_path\n            batch_dict = to_device(batch_dict, device=model.device)\n            VIS_Aug_CallbackAPI\n            # if macs is None:\n            #     from detectron2.utils.analysis import (\n            #         FlopCountAnalysis,\n            #     )\n            #     flops = FlopCountAnalysis(model, batch_dict, inference_func=lambda model, *inputs: model.sample(*inputs))\n            #     total_flops = flops.total()\n            #     # counts = flops.by_operator()\n            #     logging.debug(f'macs: {total_flops/ (10**9) / len(request_anns)}')\n            model_outputs = model.sample(batch_dict) \n            predictions = {\n                'video': model_outputs['video'][0], # t 3 h w  \n                'pred_masks': [haosen for idx, haosen in enumerate(model_outputs['pred_masks'][0]) if request_anns[idx]], # list[nt h w], t'\n                'pred_class': [haosen for idx, haosen in enumerate(model_outputs['pred_class'][0]) if request_anns[idx]], # list[nt c], t', \n            }  \n            if 'pred_boxes' in model_outputs: \n                predictions.update({'pred_boxes':  [haosen for idx, haosen in enumerate(model_outputs['pred_boxes'][0]) if request_anns[idx]]}) # # list[nt 4], t,\n            for cardib in callback_fns:\n                predictions = cardib(predictions) \n            pred_masks = predictions['pred_masks']\n            pred_class = predictions['pred_class']\n            assert len(frame_strs) == len(pred_masks)\n\n            for idx, (fname, fmk, fclass) in enumerate(zip(frame_strs, pred_masks, pred_class)):\n                VIS_Evaluator_OutAPI_EvalFn_API\n                frame_pred = {'masks': fmk, 'classes': fclass.tolist(), 'video_id': video_id, 'frame_name': fname}\n                if 'pred_boxes' in predictions:\n                    frame_pred.update({'boxes': predictions['pred_boxes'][idx]})\n\n                meta_key_metrics = {}                \n                for metric_fn in self.frame_metric_fns:\n                    metric_values = metric_fn(frame_pred=frame_pred, output_dir=evaluator_path)\n                    for key, value in metric_values.items():\n                        assert key not in meta_key_metrics\n                        meta_key_metrics[key] = value\n\n                assert fname not in metrics_by_video_id_frame[video_id]\n                metrics_by_video_id_frame[video_id][fname] = meta_key_metrics\n\n        metrics_by_video_id_frame = comm.gather(dict(metrics_by_video_id_frame), dst=0)\n        eval_metrics = {}\n        if comm.is_main_process():\n            metrics_by_video = {} \n            for video_id in tqdm(self.eval_meta_keys.keys(), desc='gathering different processes'):\n                video_id_metrics = [haosen[video_id] for haosen in metrics_by_video_id_frame if video_id in haosen]\n\n                video_id_frame_names = [list(haosen.keys()) for haosen in video_id_metrics]\n                merged_video_id_frame_names = [item for sublist in video_id_frame_names for item in sublist]\n                assert len(set(merged_video_id_frame_names)) == len(merged_video_id_frame_names),''\n                assert set(merged_video_id_frame_names).issubset(set(self.eval_meta_keys[video_id]))\n                assert set(self.eval_meta_keys[video_id]).issubset(set(merged_video_id_frame_names))\n\n                # perframe metrics frame: predictions\n                vid_frame_metrics = video_id_metrics[0]\n                for haosen in video_id_metrics[1:]:\n                    vid_frame_metrics.update(haosen)\n                metrics_by_video[video_id] = vid_frame_metrics\n\n            eval_metrics = self.metrics_aggregator(metrics_by_video)\n        comm.synchronize() \n        return eval_metrics\n\n"
  },
  {
    "path": "data_schedule/vis/evaluator_utils.py",
    "content": "_vis_metric_entrypoints = {}\n\ndef register_vis_metric(fn):\n    vis_metric_name = fn.__name__\n    if vis_metric_name in _vis_metric_entrypoints:\n        raise ValueError(f'vis_metric name {vis_metric_name} has been registered')\n    _vis_metric_entrypoints[vis_metric_name] = fn\n\n    return fn\n\ndef vis_metric_entrypoint(vis_metric_name):\n    try:\n        return _vis_metric_entrypoints[vis_metric_name]\n    except KeyError as e:\n        print(f'vis_metric Name {vis_metric_name} not found')\n\nimport numpy as np\n_EPS = np.spacing(1)\n_TYPE = np.float64\n\ndef _prepare_data(pred: np.ndarray, gt: np.ndarray) -> tuple:\n    gt = gt > 128\n    pred = pred / 255\n    if pred.max() != pred.min():\n        pred = (pred - pred.min()) / (pred.max() - pred.min())\n    return pred, gt\n\n\n\n\nclass Smeasure(object):\n    def __init__(self, length, alpha: float = 0.5):\n        self.sms = []\n        self.alpha = alpha\n\n    def step(self, pred: np.ndarray, gt: np.ndarray, idx):\n        pred, gt = _prepare_data(pred=pred, gt=gt)\n\n        sm = self.cal_sm(pred, gt)\n        self.sms.append(sm)\n\n    def cal_sm(self, pred: np.ndarray, gt: np.ndarray) -> float:\n        y = np.mean(gt)\n        if y == 0:\n            sm = 1 - np.mean(pred)\n        elif y == 1:\n            sm = np.mean(pred)\n        else:\n            sm = self.alpha * self.object(pred, gt) + (1 - self.alpha) * self.region(pred, gt)\n            sm = max(0, sm)\n        return sm\n\n    def object(self, pred: np.ndarray, gt: np.ndarray) -> float:\n        fg = pred * gt\n        bg = (1 - pred) * (1 - gt)\n        u = np.mean(gt)\n        object_score = u * self.s_object(fg, gt) + (1 - u) * self.s_object(bg, 1 - gt)\n        return object_score\n\n    def s_object(self, pred: np.ndarray, gt: np.ndarray) -> float:\n        x = np.mean(pred[gt == 1])\n        sigma_x = np.std(pred[gt == 1], ddof=1)\n        score = 2 * x / (np.power(x, 2) + 1 + sigma_x + _EPS)\n        return score\n\n    def region(self, pred: np.ndarray, gt: np.ndarray) -> float:\n        x, y = self.centroid(gt)\n        part_info = self.divide_with_xy(pred, gt, x, y)\n        w1, w2, w3, w4 = part_info['weight']\n        pred1, pred2, pred3, pred4 = part_info['pred']\n        gt1, gt2, gt3, gt4 = part_info['gt']\n        score1 = self.ssim(pred1, gt1)\n        score2 = self.ssim(pred2, gt2)\n        score3 = self.ssim(pred3, gt3)\n        score4 = self.ssim(pred4, gt4)\n\n        return w1 * score1 + w2 * score2 + w3 * score3 + w4 * score4\n\n    def centroid(self, matrix: np.ndarray) -> tuple:\n        \"\"\"\n        To ensure consistency with the matlab code, one is added to the centroid coordinate,\n        so there is no need to use the redundant addition operation when dividing the region later,\n        because the sequence generated by ``1:X`` in matlab will contain ``X``.\n        :param matrix: a bool data array\n        :return: the centroid coordinate\n        \"\"\"\n        h, w = matrix.shape\n        area_object = np.count_nonzero(matrix)\n        if area_object == 0:\n            x = np.round(w / 2)\n            y = np.round(h / 2)\n        else:\n            # More details can be found at: https://www.yuque.com/lart/blog/gpbigm\n            y, x = np.argwhere(matrix).mean(axis=0).round()\n        return int(x) + 1, int(y) + 1\n\n    def divide_with_xy(self, pred: np.ndarray, gt: np.ndarray, x, y) -> dict:\n        h, w = gt.shape\n        area = h * w\n\n        gt_LT = gt[0:y, 0:x]\n        gt_RT = gt[0:y, x:w]\n        gt_LB = gt[y:h, 0:x]\n        gt_RB = gt[y:h, x:w]\n\n        pred_LT = pred[0:y, 0:x]\n        pred_RT = pred[0:y, x:w]\n        pred_LB = pred[y:h, 0:x]\n        pred_RB = pred[y:h, x:w]\n\n        w1 = x * y / area\n        w2 = y * (w - x) / area\n        w3 = (h - y) * x / area\n        w4 = 1 - w1 - w2 - w3\n\n        return dict(gt=(gt_LT, gt_RT, gt_LB, gt_RB),\n                    pred=(pred_LT, pred_RT, pred_LB, pred_RB),\n                    weight=(w1, w2, w3, w4))\n\n    def ssim(self, pred: np.ndarray, gt: np.ndarray) -> float:\n        h, w = pred.shape\n        N = h * w\n\n        x = np.mean(pred)\n        y = np.mean(gt)\n\n        sigma_x = np.sum((pred - x) ** 2) / (N - 1)\n        sigma_y = np.sum((gt - y) ** 2) / (N - 1)\n        sigma_xy = np.sum((pred - x) * (gt - y)) / (N - 1)\n\n        alpha = 4 * x * y * sigma_xy\n        beta = (x ** 2 + y ** 2) * (sigma_x + sigma_y)\n\n        if alpha != 0:\n            score = alpha / (beta + _EPS)\n        elif alpha == 0 and beta == 0:\n            score = 1\n        else:\n            score = 0\n        return score\n\n    def get_results(self):\n        sm = np.mean(np.array(self.sms, dtype=_TYPE))\n        return dict(Smeasure=sm)\n\n\nimport torch  \nimport os\nfrom PIL import Image\n\n@register_vis_metric\ndef mask_dice_iou(frame_pred, dataset_meta, **kwargs):\n    video_id = frame_pred['video_id']\n    frame_name = frame_pred['frame_name']\n    masks = frame_pred['masks'] # nq h w\n    get_frames_gt_mask_fn = dataset_meta.get('get_frames_gt_mask_fn')\n    scores = torch.tensor(frame_pred['classes']) # nq c\n    foreground_scores = scores[:, :-1].sum(-1) # nq\n    max_idx = foreground_scores.argmax()\n    pred_mask = masks[max_idx].int() # h w\n\n    gt_mask, _ = get_frames_gt_mask_fn(video_id=video_id, frames=[frame_name]) # 1 h w\n    gt_mask = gt_mask[0].int() # h w\n\n    inter, union    = (pred_mask*gt_mask).sum(), (pred_mask+gt_mask).sum()\n    dice = (2*inter+1)/(union+1)\n    iou = (inter+1)/(union-inter+1)\n\n    return {'dice': dice, 'iou': iou}\n\n\n@register_vis_metric\ndef mask_dice_iou_sen_mae_smeasure(frame_pred, dataset_meta, **kwargs):\n    video_id = frame_pred['video_id']\n    frame_name = frame_pred['frame_name']\n    masks = frame_pred['masks'] # nq h w\n    get_frames_gt_mask_fn = dataset_meta.get('get_frames_gt_mask_fn')\n    scores = torch.tensor(frame_pred['classes']) # nq c\n    foreground_scores = scores[:, :-1].sum(-1) # nq\n    max_idx = foreground_scores.argmax()\n    pred_mask = masks[max_idx].int() # h w\n\n    gt_mask, _ = get_frames_gt_mask_fn(video_id=video_id, frames=[frame_name]) # 1 h w\n    gt_mask = gt_mask[0].int() # h w\n\n    # tp, tp*2 + fp + fn\n    inter, union    = (pred_mask*gt_mask).sum(), (pred_mask+gt_mask).sum()\n    dice = (2*inter+1)/(union+1) # 2*tp / tp + tp + fp + fn\n    iou = (inter+1)/(union-inter+1) # tp / tp + fp + fn\n\n\n    tp = (pred_mask * gt_mask).sum().float()\n    fp = (pred_mask.sum() - tp).float()\n    fn = (gt_mask.sum() - tp).float()\n    tn = (pred_mask.shape[0] * pred_mask.shape[1] - (tp + fp + fn)).float()\n    their_dice = tp * 2 / (tp + fp + fn + tp)\n    their_iou = tp / (tp + fp + fn)\n    # their_spe = tn / (tn + fp)\n    their_sen = tp / (tp + fn)\n    their_mae = (pred_mask.float() - gt_mask.float()).abs().mean()\n    \n    Np = gt_mask.sum()\n    Nn = gt_mask.shape[0] * gt_mask.shape[1] - Np\n    \n    null = Smeasure(length=1, alpha=0.5)\n    null.step(pred=(pred_mask.float() * 255 ).numpy(), gt=(gt_mask.float() * 255).numpy(), idx=None)\n    their_smeasure = torch.tensor(null.get_results()['Smeasure']).float()\n    return {'dice': dice, 'iou': iou,\n            'their_dice': their_dice,\n            'their_iou': their_iou,\n            'their_sen': their_sen,\n            'their_mae_abs': their_mae,\n            'their_smeasure': their_smeasure,\n            \n            'tp': tp, # true  positive\n            'fp': fp, # false positive\n            'fn': fn, # false negative\n            'tn': tn, # true  negative\n            'Np': Np, # positive accumulation\n            'Nn': Nn} # negative accumulation\n\n@register_vis_metric\ndef web(frame_pred, output_dir, **kwargs):\n\n    os.makedirs(os.path.join(output_dir, 'web'), exist_ok=True) \n    video_id = frame_pred['video_id']\n    frame_name = frame_pred['frame_name']\n    masks = frame_pred['masks'] # nq h w\n\n    scores = torch.tensor(frame_pred['classes']) # nq c\n    foreground_scores = scores[:, :-1].sum(-1) # nq\n    max_idx = foreground_scores.argmax()\n    pred_mask = masks[max_idx].int() # h w\n\n    mask = Image.fromarray(255 * pred_mask.int().numpy()).convert('L')\n    save_path = os.path.join(output_dir, 'web', video_id)\n\n    os.makedirs(save_path, exist_ok=True)\n    png_path = os.path.join(save_path, f'{frame_name}.png')\n    if os.path.exists(png_path):\n        os.remove(png_path)\n    mask.save(png_path)\n    return {}\n\n\n"
  },
  {
    "path": "data_schedule/vis/fibroid/__init__.py",
    "content": "# 注册fibrois数据集\nfrom . import fibroid_dataset\n\n# 注册fibroid评估标准\nfrom . import evals"
  },
  {
    "path": "data_schedule/vis/fibroid/evals.py",
    "content": "from data_schedule.vis.evaluator_utils import register_vis_metric\nimport os\nfrom glob import glob\nfrom tqdm import tqdm\nimport shutil\nfrom functools import partial\nfrom PIL import Image\nimport numpy as np\nimport torch\nimport detectron2.utils.comm as comm\nimport logging\nimport pycocotools.mask as mask_util\nfrom pycocotools.mask import decode as decode_rle\nimport data_schedule.vis.fibroid.metrics as metrics\n\n@register_vis_metric\ndef fibroid_other_medi(model_preds, \n                     dataset_meta,\n                     **kwargs):\n    assert comm.is_main_process()\n\n    iou_by_test_sample = []\n    dice_by_test_sample = []\n    preds_by_test_sample = []\n    gt_by_test_sample = []\n    get_frames_gt_mask_fn = dataset_meta.get('get_frames_gt_mask_fn')\n\n    for pred in model_preds:\n        video_id = pred['video_id'] # str\n        frame_name = pred['frame_name'] # list[str], t'\n        masks = pred['masks']# list[rle], nq\n        scores = pred['scores'] # nq\n\n        max_idx = torch.tensor(scores).argmax()\n        pred_mask = masks[max_idx] # rle\n        pred_mask = decode_rle(pred_mask)\n        pred_mask = torch.as_tensor(pred_mask, dtype=torch.uint8).contiguous() # h w\n\n        gt_mask, _ = get_frames_gt_mask_fn(video_id=video_id, frames=[frame_name]) # 1 h w\n        gt_mask = gt_mask[0].int() # 0/1\n\n        preds_by_test_sample.append(pred_mask)\n        gt_by_test_sample.append(gt_mask)\n\n        tp, fp, fn, tn = metrics.get_stats(pred_mask[None, None, ...], gt_mask[None, None, ...], \n                                           mode='binary')\n        iou_score = metrics.iou_score(tp, fp, fn, tn, reduction='micro')\n        dice = metrics.dice(tp, fp, fn, tn, reduction='micro')\n\n        iou_by_test_sample.append(iou_score)\n        dice_by_test_sample.append(dice)\n\n    mean_iou = torch.tensor(iou_by_test_sample).mean()\n    mean_dice = torch.tensor(dice_by_test_sample).mean()\n\n    preds_by_test_sample = torch.stack(preds_by_test_sample, dim=0).unsqueeze(1) # N 1 h w\n    gt_by_test_sample = torch.stack(gt_by_test_sample, dim=0).unsqueeze(1) # N 1 h w\n\n    tp, fp, fn, tn = metrics.get_stats(preds_by_test_sample, gt_by_test_sample, \n                                        mode='binary')\n    overall_iou = metrics.iou_score(tp, fp, fn, tn, reduction='micro')    \n    recall = metrics.recall(tp, fp, fn, tn, reduction='micro-imagewise') \n    precision = metrics.precision(tp, fp, fn, tn, reduction='micro-imagewise')\n\n    all_medi = {\n        'mean_iou': mean_iou,\n        'dice': mean_dice,\n        'overall_iou': overall_iou, # J/overallIoU\n        'recall': recall,\n        'precision': precision,\n        'F': 2 * precision * recall / (precision + recall)\n    }  \n    return all_medi   \n    \n\nfrom collections import defaultdict\n\n# by_vid, by_frame\niou_dict = defaultdict(dict)\n\n@register_vis_metric\ndef fibroid_mask_dice_iou(frame_pred, dataset_meta, **kwargs):\n    video_id = frame_pred['video_id']\n    frame_name = frame_pred['frame_name']\n    masks = frame_pred['masks'] # nq h w\n    get_frames_gt_mask_fn = dataset_meta.get('get_frames_gt_mask_fn')\n    scores = torch.tensor(frame_pred['classes']) # nq c, 保证c是2\n    foreground_scores = scores[:, :-1].sum(-1) # nq\n    max_idx = foreground_scores.argmax()\n    pred_mask = masks[max_idx].int() # h w\n\n    gt_mask, _ = get_frames_gt_mask_fn(video_id=video_id, frames=[frame_name]) # 1 h w\n    gt_mask = gt_mask[0].int() # h w\n\n    inter, union    = (pred_mask*gt_mask).sum(), (pred_mask+gt_mask).sum()\n    dice = (2*inter+1)/(union+1)\n    iou = (inter+1)/(union-inter+1)\n    iou_dict[video_id][frame_name] = iou\n    if iou > 0.6:\n        print(f'video_id: {video_id}, frame: {frame_name}: dice {dice}, iou {iou}')\n    return {'dice': dice, 'iou': iou}\n\n@register_vis_metric\ndef fibroid_metric_aggregator(metrics_by_vid_frame, dataset_meta, eval_meta_keys, **kwargs):\n    # output: eval_metrics\n    # video: frame_name: metric/ vid_metrics\n\n    eval_metrics = {}\n    # video, frame_name\n    # perframe metrics\n    metric_names = metrics_by_vid_frame[list(eval_meta_keys.keys())[0]][eval_meta_keys[list(eval_meta_keys.keys())[0]][0]]\n    for taylor_swift in metric_names:\n        eval_metrics[taylor_swift] = torch.tensor([metrics_by_vid_frame[video][frame][taylor_swift]  for video in eval_meta_keys.keys() for frame in eval_meta_keys[video]]).mean()\n    \n    # metrics by each video\n    mean_iou_by_each_video = {}\n    mean_dice_by_each_video = {}\n    for video in eval_meta_keys:\n        mean_iou_by_each_video[video] = torch.tensor([metrics_by_vid_frame[video][fname]['iou'] for fname in eval_meta_keys[video]]).mean()\n        mean_dice_by_each_video[video] = torch.tensor([metrics_by_vid_frame[video][fname]['dice'] for fname in eval_meta_keys[video]]).mean()\n    \n    mean_iou_by_each_video = dict(sorted(mean_iou_by_each_video.items(), key=lambda x: x[1]))\n    mean_dice_by_each_video = dict(sorted(mean_dice_by_each_video.items(), key=lambda x: x[1]))\n    logging.debug(f'mean_iou_by_each_video: {mean_iou_by_each_video}')\n    logging.debug(f'mean_dice_by_each_video: {mean_dice_by_each_video}')\n    return eval_metrics\n\n"
  },
  {
    "path": "data_schedule/vis/fibroid/fibroid_dataset.py",
    "content": "from typing import Optional, Union\nimport json\nimport os\nfrom functools import partial\nimport numpy as np\nimport torch\nimport logging\nfrom tqdm import tqdm\nimport copy\nfrom detectron2.data import DatasetCatalog, MetadataCatalog\nfrom collections import defaultdict\nfrom data_schedule.vis.apis import VIS_Dataset\nfrom .fibroid_utils import get_frames, get_frames_mask, SET_NAME_TO_DIR,\\\n      SET_NAME, SET_NAME_TO_NUM_VIDEOS, SET_NAME_TO_MODE, SET_NAME_TO_PREFIX, SET_NAME_TO_GT_TYPE\n\ndef fibroid_train(step_size, # none / int; 0, 6, 13, 19 ...\n                  split_dataset_name,\n                  video_ids,\n                  video_to_frames):\n    logging.debug(f'{split_dataset_name} Generating metas...')   \n    metas = []\n    for vid_id in tqdm(video_ids):\n        all_frames = sorted(video_to_frames[vid_id])\n        if step_size is None: \n            metas.append({\n                'video_id': vid_id,\n                'all_frames' : all_frames,\n                'meta_idx': len(metas),\n                'all_objs': {1: {'class_label': 0,}} # 语义分割\n            }) \n        else:\n            for frame_idx in range(0, len(all_frames), step_size):\n                metas.append({\n                    'video_id': vid_id,\n                    'frame_idx': frame_idx,\n                    'all_frames': all_frames,\n                    'all_objs': {1: {'class_label': 0,}},\n                    'meta_idx': len(metas)\n                })                \n\n    logging.debug(f'{split_dataset_name} Total metas: [{len(metas)}]')\n    return metas\n\ndef fibroid_evaluate(eval_video_ids,\n                     split_dataset_name,\n                     step_size,\n                     video_to_frames):\n    if (step_size is not None) and (step_size > 1):\n        logging.warning('为什么 evaluate的时候step size大于1呢')\n        raise ValueError()\n    metas = []\n    for video_id in eval_video_ids:\n        VIS_Dataset\n        all_frames = sorted(video_to_frames[video_id])\n        if step_size == None:\n            metas.append({\n                'video_id': video_id,\n                'all_frames': all_frames,\n                'meta_idx': len(metas)\n            })     \n    \n        else:   \n            for frame_idx in range(0, len(all_frames), step_size):\n                metas.append({\n                    'video_id': video_id,\n                    'frame_idx': frame_idx,\n                    'all_frames': all_frames,\n                    'meta_idx': len(metas)\n                })                                 \n\n    logging.debug(f'{split_dataset_name} Total metas: [{len(metas)}]')  \n    return metas\n\n\n_root = os.getenv('DATASET_PATH')\nroot = os.path.join(_root, 'uterus_myoma/Dataset')\nvisualize_meta_idxs = defaultdict(list)\nvisualize_meta_idxs['fibroid_train_step[6]'] = [] \nvisualize_meta_idxs['fibroid_train'] = [] \nvisualize_meta_idxs['fibroid_train_ste[1]'] = [] \nvisualize_meta_idxs['fibroid_validate'] = []\nvisualize_meta_idxs['fibroid_validate_step[1]'] = []\nvisualize_meta_idxs['weakPolyP_fibroid_validate_step[1]'] = [] \n\nfibroid_meta = {\n    'thing_classes': ['rumor', 'not rumor'],\n    'thing_colors': [(255., 140., 0.), (0., 255., 0.)],\n}\n\nfor name in SET_NAME:\n    set_dir = SET_NAME_TO_DIR[name] \n    set_dir = os.path.join(root, set_dir)\n    num_videos = SET_NAME_TO_NUM_VIDEOS[name]\n\n    video_ids = os.listdir(os.path.join(set_dir, 'Frame'))\n    assert len(video_ids) == num_videos\n\n    video_to_frames = {\n          vid: sorted([png[:-4] for png in os.listdir(os.path.join(set_dir, 'Frame', vid)) if png.endswith('.png')])\\\n            for vid in video_ids\n    }\n    mode = SET_NAME_TO_MODE[name]\n    prefix = SET_NAME_TO_PREFIX[name]\n\n    if mode == 'train':\n        train_meta = copy.deepcopy(fibroid_meta)\n        gt_type = SET_NAME_TO_GT_TYPE[name]\n        train_meta.update({\n            'mode': 'train',\n            'get_frames_fn': partial(get_frames, frames_path=os.path.join(set_dir, 'Frame')),\n            'get_frames_mask_fn': partial(get_frames_mask, mask_path=os.path.join(set_dir, gt_type),),\n            'get_frames_gt_mask_fn': partial(get_frames_mask, mask_path=os.path.join(root, os.path.join(set_dir, 'GT')),),\n        })\n        # train\n        for step_size in [1, 6, None]:\n            step_identifer = '' if step_size is None else f'_step[{step_size}]'\n            split_name = f'{prefix}{step_identifer}'\n            train_meta.update({'name': split_name})\n            DatasetCatalog.register(split_name, partial(fibroid_train,\n                                                        video_ids=video_ids, \n                                                        split_dataset_name=split_name,\n                                                        step_size=step_size,\n                                                        video_to_frames=video_to_frames,))    \n            MetadataCatalog.get(split_name).set(**train_meta, \n                                                step_size=step_size,\n                                                visualize_meta_idxs=visualize_meta_idxs[split_name])         \n\n\n    elif mode == 'evaluate':\n        prefix = SET_NAME_TO_PREFIX[name]\n        validate_meta = copy.deepcopy(fibroid_meta)\n\n        validate_meta.update({\n            'mode': 'evaluate',\n            'get_frames_fn': partial(get_frames, frames_path=os.path.join(root, os.path.join(set_dir, 'Frame'))),\n            'eval_set_name': SET_NAME_TO_DIR[name],\n            'get_frames_gt_mask_fn': partial(get_frames_mask, mask_path=os.path.join(root, os.path.join(set_dir, 'GT')),),\n            'eval_meta_keys': video_to_frames\n        })\n        # validate\n        for step_size in  [1, None,]:\n            step_identifer = '' if step_size is None else f'_step[{step_size}]'\n            split_name = f'{prefix}{step_identifer}'\n            validate_meta.update({'name': split_name})\n            DatasetCatalog.register(split_name, partial(fibroid_evaluate,\n                                                        eval_video_ids=video_ids, \n                                                        split_dataset_name=split_name, \n                                                        step_size=step_size,\n                                                        video_to_frames=video_to_frames))    \n            MetadataCatalog.get(split_name).set(**validate_meta, step_size=step_size,\n                                                visualize_meta_idxs=visualize_meta_idxs[split_name])\n                \n\n\n \n\n"
  },
  {
    "path": "data_schedule/vis/fibroid/fibroid_utils.py",
    "content": "import wandb\nimport plotly.express as px\nimport logging\nimport os\nimport numpy as np\nimport torch\nimport json\nfrom joblib import Parallel, delayed\nimport multiprocessing\nimport torch.distributed as dist\nimport detectron2.utils.comm as comm\n\nimport pycocotools.mask as mask_util\nfrom pycocotools.mask import encode, area\n\nfrom data_schedule.utils.segmentation import bounding_box_from_mask\nfrom data_schedule.utils.video_clips import generate_windows_of_video\nfrom glob import glob\nfrom PIL import Image\n\ndef get_frames(frames_path, video_id, frames):\n    return [Image.open(os.path.join(frames_path, video_id, f'{f}.png'),).convert('RGB') for f in frames]\n\n# t' h w, int, obj_ids ;  has_ann t\ndef get_frames_mask(mask_path, video_id, frames):\n    masks = [Image.open(os.path.join(mask_path, video_id, f'{f}.png')).convert('L') for f in frames]\n    masks = [np.array(mk) for mk in masks]\n    masks = torch.stack([torch.from_numpy(mk) for mk in masks], dim=0) # t h w\n    masks = (masks > 0).int()\n    return masks, torch.ones(len(frames)).bool()\n\n\nSET_NAME = [\n        'fibroid_train', \n        'fibroid_validate',\n        'weakpolyp_train',\n\n        'fibroid_validate_temp7',\n        'fibroid_train_temp7',\n        # 'weakpolyp_fibroid_train_temp7',\n\n        'fibroid_validate_temp8',\n        'fibroid_train_temp8',\n        'weakpolyp_fibroid_train_temp8'\n         ]\n\nSET_NAME_TO_DIR = {\n    'fibroid_train': 'temp/train',\n    'fibroid_validate': 'temp/test',\n    'weakpolyp_train': 'temp/uterus_myoma_WeakPolyP_temp/train',\n\n\n    'fibroid_validate_temp7': 'temp7/test',\n    'fibroid_train_temp7': 'temp7/train',\n    'weakpolyp_fibroid_train_temp7': 'temp7/uterus_myoma_WeakPolyP_temp7/train',\n\n    'fibroid_validate_temp8': 'temp8/test',\n    'fibroid_train_temp8': 'temp8/train',\n    'weakpolyp_fibroid_train_temp8': 'temp8/uterus_myoma_WeakPolyP_temp8/train',\n\n}\n\nSET_NAME_TO_NUM_VIDEOS = {\n    'fibroid_train': 80,\n    'fibroid_validate': 20,\n    'weakpolyp_train': 80,\n\n\n    'fibroid_train_temp7': 85,\n    'fibroid_validate_temp7': 15,\n    'weakpolyp_fibroid_train_temp7': 85 ,     \n\n\n    'fibroid_train_temp8': 83,\n    'fibroid_validate_temp8': 17,\n    'weakpolyp_fibroid_train_temp8': 83     \n}\n\nSET_NAME_TO_MODE = {\n    'fibroid_train': 'train',\n    'fibroid_validate': 'evaluate',\n    'weakpolyp_train': 'train',\n\n    'fibroid_train_temp7': 'train',\n    'fibroid_validate_temp7': 'evaluate',\n    'weakpolyp_fibroid_train_temp7': 'train',   \n\n    'fibroid_train_temp8': 'train',\n    'fibroid_validate_temp8': 'evaluate',\n    'weakpolyp_fibroid_train_temp8': 'train' \n}\n\nSET_NAME_TO_PREFIX = {\n    'fibroid_train': 'fibroid_train',\n    'fibroid_validate': 'fibroid_validate',\n    'weakpolyp_train': 'weakpolyp_fibroid_train',\n\n    'fibroid_train_temp7': 'fibroid_train_temp7',\n    'fibroid_validate_temp7': 'fibroid_validate_temp7',\n    'weakpolyp_fibroid_train_temp7': 'weakpolyp_fibroid_train_temp7' ,\n\n    'fibroid_train_temp8': 'fibroid_train_temp8',\n    'fibroid_validate_temp8': 'fibroid_validate_temp8',\n    'weakpolyp_fibroid_train_temp8': 'weakpolyp_fibroid_train_temp8' \n\n}\n\nSET_NAME_TO_GT_TYPE = {\n    'fibroid_train': 'GT',\n    'fibroid_validate': 'GT',\n    'weakpolyp_train': 'Box',\n\n    'fibroid_train_temp7': 'GT',\n    'fibroid_validate_temp7': 'GT',\n    'weakpolyp_fibroid_train_temp7': 'Box', \n\n    'fibroid_train_temp8': 'GT',\n    'fibroid_validate_temp8': 'GT',\n    'weakpolyp_fibroid_train_temp8': 'Box' \n}\n"
  },
  {
    "path": "data_schedule/vis/fibroid/metrics.py",
    "content": "import warnings\nfrom typing import Optional, List, Tuple, Union\nimport torch\n\n\"\"\"Various metrics based on Type I and Type II errors.\n\nReferences:\n    https://en.wikipedia.org/wiki/Confusion_matrix\n\n\nExample:\n\n    .. code-block:: python\n\n        import segmentation_models_pytorch as smp\n\n        # lets assume we have multilabel prediction for 3 classes\n        output = torch.rand([10, 3, 256, 256])\n        target = torch.rand([10, 3, 256, 256]).round().long()\n\n        # first compute statistics for true positives, false positives, false negative and\n        # true negative \"pixels\"\n        tp, fp, fn, tn = smp.metrics.get_stats(output, target, mode='multilabel', threshold=0.5)\n\n        # then compute metrics with required reduction (see metric docs)\n        iou_score = smp.metrics.iou_score(tp, fp, fn, tn, reduction=\"micro\")\n        f1_score = smp.metrics.f1_score(tp, fp, fn, tn, reduction=\"micro\")\n        f2_score = smp.metrics.fbeta_score(tp, fp, fn, tn, beta=2, reduction=\"micro\")\n        accuracy = smp.metrics.accuracy(tp, fp, fn, tn, reduction=\"macro\")\n        recall = smp.metrics.recall(tp, fp, fn, tn, reduction=\"micro-imagewise\")\n\n\"\"\"\n\n\n__all__ = [\n    \"get_stats\",\n    \"fbeta_score\",\n    \"f1_score\",\n    \"iou_score\",\n    \"accuracy\",\n    \"precision\",\n    \"recall\",\n    \"sensitivity\",\n    \"specificity\",\n    \"balanced_accuracy\",\n    \"positive_predictive_value\",\n    \"negative_predictive_value\",\n    \"false_negative_rate\",\n    \"false_positive_rate\",\n    \"false_discovery_rate\",\n    \"false_omission_rate\",\n    \"positive_likelihood_ratio\",\n    \"negative_likelihood_ratio\",\n]\n\n\n###################################################################################################\n# Statistics computation (true positives, false positives, false negatives, false positives)\n###################################################################################################\n\n\ndef get_stats(\n    output: Union[torch.LongTensor, torch.FloatTensor],\n    target: torch.LongTensor,\n    mode: str,\n    ignore_index: Optional[int] = None,\n    threshold: Optional[Union[float, List[float]]] = None,\n    num_classes: Optional[int] = None,\n) -> Tuple[torch.LongTensor, torch.LongTensor, torch.LongTensor, torch.LongTensor]:\n    \"\"\"Compute true positive, false positive, false negative, true negative 'pixels'\n    for each image and each class.\n\n    Args:\n        output (Union[torch.LongTensor, torch.FloatTensor]): Model output with following\n            shapes and types depending on the specified ``mode``:\n\n            'binary'\n                shape (N, 1, ...) and ``torch.LongTensor`` or ``torch.FloatTensor``\n\n            'multilabel'\n                shape (N, C, ...) and ``torch.LongTensor`` or ``torch.FloatTensor``\n\n            'multiclass'\n                shape (N, ...) and ``torch.LongTensor``\n\n        target (torch.LongTensor): Targets with following shapes depending on the specified ``mode``:\n\n            'binary'\n                shape (N, 1, ...)\n\n            'multilabel'\n                shape (N, C, ...)\n\n            'multiclass'\n                shape (N, ...)\n\n        mode (str): One of ``'binary'`` | ``'multilabel'`` | ``'multiclass'``\n        ignore_index (Optional[int]): Label to ignore on for metric computation.\n            **Not** supproted for ``'binary'`` and ``'multilabel'`` modes.  Defaults to None.\n        threshold (Optional[float, List[float]]): Binarization threshold for\n            ``output`` in case of ``'binary'`` or ``'multilabel'`` modes. Defaults to None.\n        num_classes (Optional[int]): Number of classes, necessary attribute\n            only for ``'multiclass'`` mode. Class values should be in range 0..(num_classes - 1).\n            If ``ignore_index`` is specified it should be outside the classes range, e.g. ``-1`` or\n            ``255``.\n\n    Raises:\n        ValueError: in case of misconfiguration.\n\n    Returns:\n        Tuple[torch.LongTensor]: true_positive, false_positive, false_negative,\n            true_negative tensors (N, C) shape each.\n\n    \"\"\"\n\n    if torch.is_floating_point(target):\n        raise ValueError(f\"Target should be one of the integer types, got {target.dtype}.\")\n\n    if torch.is_floating_point(output) and threshold is None:\n        raise ValueError(\n            f\"Output should be one of the integer types if ``threshold`` is not None, got {output.dtype}.\"\n        )\n\n    if torch.is_floating_point(output) and mode == \"multiclass\":\n        raise ValueError(f\"For ``multiclass`` mode ``output`` should be one of the integer types, got {output.dtype}.\")\n\n    if mode not in {\"binary\", \"multiclass\", \"multilabel\"}:\n        raise ValueError(f\"``mode`` should be in ['binary', 'multiclass', 'multilabel'], got mode={mode}.\")\n\n    if mode == \"multiclass\" and threshold is not None:\n        raise ValueError(\"``threshold`` parameter does not supported for this 'multiclass' mode\")\n\n    if output.shape != target.shape:\n        raise ValueError(\n            \"Dimensions should match, but ``output`` shape is not equal to ``target`` \"\n            + f\"shape, {output.shape} != {target.shape}\"\n        )\n\n    if mode != \"multiclass\" and ignore_index is not None:\n        raise ValueError(f\"``ignore_index`` parameter is not supproted for '{mode}' mode\")\n\n    if mode == \"multiclass\" and num_classes is None:\n        raise ValueError(\"``num_classes`` attribute should be not ``None`` for 'multiclass' mode.\")\n\n    if ignore_index is not None and 0 <= ignore_index <= num_classes - 1:\n        raise ValueError(\n            f\"``ignore_index`` should be outside the class values range, but got class values in range \"\n            f\"0..{num_classes - 1} and ``ignore_index={ignore_index}``. Hint: if you have ``ignore_index = 0``\"\n            f\"consirder subtracting ``1`` from your target and model output to make ``ignore_index = -1``\"\n            f\"and relevant class values started from ``0``.\"\n        )\n\n    if mode == \"multiclass\":\n        tp, fp, fn, tn = _get_stats_multiclass(output, target, num_classes, ignore_index)\n    else:\n        if threshold is not None:\n            output = torch.where(output >= threshold, 1, 0)\n            target = torch.where(target >= threshold, 1, 0)\n        tp, fp, fn, tn = _get_stats_multilabel(output, target)\n\n    return tp, fp, fn, tn\n\n\n@torch.no_grad()\ndef _get_stats_multiclass(\n    output: torch.LongTensor,\n    target: torch.LongTensor,\n    num_classes: int,\n    ignore_index: Optional[int],\n) -> Tuple[torch.LongTensor, torch.LongTensor, torch.LongTensor, torch.LongTensor]:\n\n    batch_size, *dims = output.shape\n    num_elements = torch.prod(torch.tensor(dims)).long()\n\n    if ignore_index is not None:\n        ignore = target == ignore_index\n        output = torch.where(ignore, -1, output)\n        target = torch.where(ignore, -1, target)\n        ignore_per_sample = ignore.view(batch_size, -1).sum(1)\n\n    tp_count = torch.zeros(batch_size, num_classes, dtype=torch.long)\n    fp_count = torch.zeros(batch_size, num_classes, dtype=torch.long)\n    fn_count = torch.zeros(batch_size, num_classes, dtype=torch.long)\n    tn_count = torch.zeros(batch_size, num_classes, dtype=torch.long)\n\n    for i in range(batch_size):\n        target_i = target[i]\n        output_i = output[i]\n        mask = output_i == target_i\n        matched = torch.where(mask, target_i, -1)\n        tp = torch.histc(matched.float(), bins=num_classes, min=0, max=num_classes - 1)\n        fp = torch.histc(output_i.float(), bins=num_classes, min=0, max=num_classes - 1) - tp\n        fn = torch.histc(target_i.float(), bins=num_classes, min=0, max=num_classes - 1) - tp\n        tn = num_elements - tp - fp - fn\n        if ignore_index is not None:\n            tn = tn - ignore_per_sample[i]\n        tp_count[i] = tp.long()\n        fp_count[i] = fp.long()\n        fn_count[i] = fn.long()\n        tn_count[i] = tn.long()\n\n    return tp_count, fp_count, fn_count, tn_count\n\n\n@torch.no_grad()\ndef _get_stats_multilabel(\n    output: torch.LongTensor,\n    target: torch.LongTensor,\n) -> Tuple[torch.LongTensor, torch.LongTensor, torch.LongTensor, torch.LongTensor]:\n\n    batch_size, num_classes, *dims = target.shape\n    output = output.view(batch_size, num_classes, -1)\n    target = target.view(batch_size, num_classes, -1)\n\n    tp = (output * target).sum(2)\n    fp = output.sum(2) - tp\n    fn = target.sum(2) - tp\n    tn = torch.prod(torch.tensor(dims)) - (tp + fp + fn)\n\n    return tp, fp, fn, tn\n\n\n###################################################################################################\n# Metrics computation\n###################################################################################################\n\n\ndef _handle_zero_division(x, zero_division):\n    nans = torch.isnan(x)\n    if torch.any(nans) and zero_division == \"warn\":\n        warnings.warn(\"Zero division in metric calculation!\")\n    value = zero_division if zero_division != \"warn\" else 0\n    value = torch.tensor(value, dtype=x.dtype).to(x.device)\n    x = torch.where(nans, value, x)\n    return x\n\n\ndef _compute_metric(\n    metric_fn,\n    tp,\n    fp,\n    fn,\n    tn,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division=\"warn\",\n    **metric_kwargs,\n) -> float:\n\n    if class_weights is None and reduction is not None and \"weighted\" in reduction:\n        raise ValueError(f\"Class weights should be provided for `{reduction}` reduction\")\n\n    class_weights = class_weights if class_weights is not None else 1.0\n    class_weights = torch.tensor(class_weights).to(tp.device)\n    class_weights = class_weights / class_weights.sum()\n\n    if reduction == \"micro\":\n        tp = tp.sum()\n        fp = fp.sum()\n        fn = fn.sum()\n        tn = tn.sum()\n        score = metric_fn(tp, fp, fn, tn, **metric_kwargs)\n\n    elif reduction == \"macro\":\n        tp = tp.sum(0)\n        fp = fp.sum(0)\n        fn = fn.sum(0)\n        tn = tn.sum(0)\n        score = metric_fn(tp, fp, fn, tn, **metric_kwargs)\n        score = _handle_zero_division(score, zero_division)\n        score = (score * class_weights).mean()\n\n    elif reduction == \"weighted\":\n        tp = tp.sum(0)\n        fp = fp.sum(0)\n        fn = fn.sum(0)\n        tn = tn.sum(0)\n        score = metric_fn(tp, fp, fn, tn, **metric_kwargs)\n        score = _handle_zero_division(score, zero_division)\n        score = (score * class_weights).sum()\n\n    elif reduction == \"micro-imagewise\":\n        tp = tp.sum(1)\n        fp = fp.sum(1)\n        fn = fn.sum(1)\n        tn = tn.sum(1)\n        score = metric_fn(tp, fp, fn, tn, **metric_kwargs)\n        score = _handle_zero_division(score, zero_division)\n        score = score.mean()\n\n    elif reduction == \"macro-imagewise\" or reduction == \"weighted-imagewise\":\n        score = metric_fn(tp, fp, fn, tn, **metric_kwargs)\n        score = _handle_zero_division(score, zero_division)\n        score = (score.mean(0) * class_weights).mean()\n\n    elif reduction == \"none\" or reduction is None:\n        score = metric_fn(tp, fp, fn, tn, **metric_kwargs)\n        score = _handle_zero_division(score, zero_division)\n\n    else:\n        raise ValueError(\n            \"`reduction` should be in [micro, macro, weighted, micro-imagewise,\"\n            + \"macro-imagesize, weighted-imagewise, none, None]\"\n        )\n\n    return score\n\n\n# Logic for metric computation, all metrics are with the same interface\n\n\ndef _fbeta_score(tp, fp, fn, tn, beta=1):\n    beta_tp = (1 + beta**2) * tp\n    beta_fn = (beta**2) * fn\n    score = beta_tp / (beta_tp + beta_fn + fp)\n    return score\n\n\ndef _iou_score(tp, fp, fn, tn):\n    return tp / (tp + fp + fn)\n\n\ndef _accuracy(tp, fp, fn, tn):\n    return (tp + tn) / (tp + fp + fn + tn)\n\n\ndef _sensitivity(tp, fp, fn, tn):\n    return tp / (tp + fn)\n\n\ndef _specificity(tp, fp, fn, tn):\n    return tn / (tn + fp)\n\n\ndef _balanced_accuracy(tp, fp, fn, tn):\n    return (_sensitivity(tp, fp, fn, tn) + _specificity(tp, fp, fn, tn)) / 2\n\ndef _dice(tp, fp, fn, tn):\n    return tp * 2 / (tp + fp + fn + tp)\n\ndef _positive_predictive_value(tp, fp, fn, tn):\n    return tp / (tp + fp)\n\n\ndef _negative_predictive_value(tp, fp, fn, tn):\n    return tn / (tn + fn)\n\n\ndef _false_negative_rate(tp, fp, fn, tn):\n    return fn / (fn + tp)\n\n\ndef _false_positive_rate(tp, fp, fn, tn):\n    return fp / (fp + tn)\n\n\ndef _false_discovery_rate(tp, fp, fn, tn):\n    return 1 - _positive_predictive_value(tp, fp, fn, tn)\n\n\ndef _false_omission_rate(tp, fp, fn, tn):\n    return 1 - _negative_predictive_value(tp, fp, fn, tn)\n\n\ndef _positive_likelihood_ratio(tp, fp, fn, tn):\n    return _sensitivity(tp, fp, fn, tn) / _false_positive_rate(tp, fp, fn, tn)\n\n\ndef _negative_likelihood_ratio(tp, fp, fn, tn):\n    return _false_negative_rate(tp, fp, fn, tn) / _specificity(tp, fp, fn, tn)\n\n\ndef fbeta_score(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    beta: float = 1.0,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"F beta score\"\"\"\n    return _compute_metric(\n        _fbeta_score,\n        tp,\n        fp,\n        fn,\n        tn,\n        beta=beta,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\n\ndef f1_score(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"F1 score\"\"\"\n    return _compute_metric(\n        _fbeta_score,\n        tp,\n        fp,\n        fn,\n        tn,\n        beta=1.0,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\ndef dice(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"IoU score or Jaccard index\"\"\"  # noqa\n    return _compute_metric(\n        _dice,\n        tp,\n        fp,\n        fn,\n        tn,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\ndef iou_score(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"IoU score or Jaccard index\"\"\"  # noqa\n    return _compute_metric(\n        _iou_score,\n        tp,\n        fp,\n        fn,\n        tn,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\n\ndef accuracy(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"Accuracy\"\"\"\n    return _compute_metric(\n        _accuracy,\n        tp,\n        fp,\n        fn,\n        tn,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\n\ndef sensitivity(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"Sensitivity, recall, hit rate, or true positive rate (TPR)\"\"\"\n    return _compute_metric(\n        _sensitivity,\n        tp,\n        fp,\n        fn,\n        tn,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\n\ndef specificity(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"Specificity, selectivity or true negative rate (TNR)\"\"\"\n    return _compute_metric(\n        _specificity,\n        tp,\n        fp,\n        fn,\n        tn,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\n\ndef balanced_accuracy(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"Balanced accuracy\"\"\"\n    return _compute_metric(\n        _balanced_accuracy,\n        tp,\n        fp,\n        fn,\n        tn,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\n\ndef positive_predictive_value(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"Precision or positive predictive value (PPV)\"\"\"\n    return _compute_metric(\n        _positive_predictive_value,\n        tp,\n        fp,\n        fn,\n        tn,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\n\ndef negative_predictive_value(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"Negative predictive value (NPV)\"\"\"\n    return _compute_metric(\n        _negative_predictive_value,\n        tp,\n        fp,\n        fn,\n        tn,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\n\ndef false_negative_rate(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"Miss rate or false negative rate (FNR)\"\"\"\n    return _compute_metric(\n        _false_negative_rate,\n        tp,\n        fp,\n        fn,\n        tn,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\n\ndef false_positive_rate(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"Fall-out or false positive rate (FPR)\"\"\"\n    return _compute_metric(\n        _false_positive_rate,\n        tp,\n        fp,\n        fn,\n        tn,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\n\ndef false_discovery_rate(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"False discovery rate (FDR)\"\"\"  # noqa\n    return _compute_metric(\n        _false_discovery_rate,\n        tp,\n        fp,\n        fn,\n        tn,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\n\ndef false_omission_rate(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"False omission rate (FOR)\"\"\"  # noqa\n    return _compute_metric(\n        _false_omission_rate,\n        tp,\n        fp,\n        fn,\n        tn,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\n\ndef positive_likelihood_ratio(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"Positive likelihood ratio (LR+)\"\"\"\n    return _compute_metric(\n        _positive_likelihood_ratio,\n        tp,\n        fp,\n        fn,\n        tn,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\n\ndef negative_likelihood_ratio(\n    tp: torch.LongTensor,\n    fp: torch.LongTensor,\n    fn: torch.LongTensor,\n    tn: torch.LongTensor,\n    reduction: Optional[str] = None,\n    class_weights: Optional[List[float]] = None,\n    zero_division: Union[str, float] = 1.0,\n) -> torch.Tensor:\n    \"\"\"Negative likelihood ratio (LR-)\"\"\"\n    return _compute_metric(\n        _negative_likelihood_ratio,\n        tp,\n        fp,\n        fn,\n        tn,\n        reduction=reduction,\n        class_weights=class_weights,\n        zero_division=zero_division,\n    )\n\n\n_doc = \"\"\"\n\n    Args:\n        tp (torch.LongTensor): tensor of shape (N, C), true positive cases\n        fp (torch.LongTensor): tensor of shape (N, C), false positive cases\n        fn (torch.LongTensor): tensor of shape (N, C), false negative cases\n        tn (torch.LongTensor): tensor of shape (N, C), true negative cases\n        reduction (Optional[str]): Define how to aggregate metric between classes and images:\n\n            - 'micro'\n                Sum true positive, false positive, false negative and true negative pixels over\n                all images and all classes and then compute score.\n\n            - 'macro'\n                Sum true positive, false positive, false negative and true negative pixels over\n                all images for each label, then compute score for each label separately and average labels scores.\n                This does not take label imbalance into account.\n\n            - 'weighted'\n                Sum true positive, false positive, false negative and true negative pixels over\n                all images for each label, then compute score for each label separately and average\n                weighted labels scores.\n\n            - 'micro-imagewise'\n                Sum true positive, false positive, false negative and true negative pixels for **each image**,\n                then compute score for **each image** and average scores over dataset. All images contribute equally\n                to final score, however takes into accout class imbalance for each image.\n\n            - 'macro-imagewise'\n                Compute score for each image and for each class on that image separately, then compute average score\n                on each image over labels and average image scores over dataset. Does not take into account label\n                imbalance on each image.\n\n            - 'weighted-imagewise'\n                Compute score for each image and for each class on that image separately, then compute weighted average\n                score on each image over labels and average image scores over dataset.\n\n            - 'none' or ``None``\n                Same as ``'macro-imagewise'``, but without any reduction.\n\n            For ``'binary'`` case ``'micro' = 'macro' = 'weighted'`` and\n            ``'micro-imagewise' = 'macro-imagewise' = 'weighted-imagewise'``.\n\n            Prefixes ``'micro'``, ``'macro'`` and ``'weighted'`` define how the scores for classes will be aggregated,\n            while postfix ``'imagewise'`` defines how scores between the images will be aggregated.\n\n        class_weights (Optional[List[float]]): list of class weights for metric\n            aggregation, in case of `weighted*` reduction is chosen. Defaults to None.\n        zero_division (Union[str, float]): Sets the value to return when there is a zero division,\n            i.e. when all predictions and labels are negative. If set to “warn”, this acts as 0,\n            but warnings are also raised. Defaults to 1.\n\n    Returns:\n        torch.Tensor: if ``'reduction'`` is not ``None`` or ``'none'`` returns scalar metric,\n            else returns tensor of shape (N, C)\n\n    References:\n        https://en.wikipedia.org/wiki/Confusion_matrix\n\"\"\"\n\nfbeta_score.__doc__ += _doc\nf1_score.__doc__ += _doc\niou_score.__doc__ += _doc\naccuracy.__doc__ += _doc\nsensitivity.__doc__ += _doc\nspecificity.__doc__ += _doc\nbalanced_accuracy.__doc__ += _doc\npositive_predictive_value.__doc__ += _doc\nnegative_predictive_value.__doc__ += _doc\nfalse_negative_rate.__doc__ += _doc\nfalse_positive_rate.__doc__ += _doc\nfalse_discovery_rate.__doc__ += _doc\nfalse_omission_rate.__doc__ += _doc\npositive_likelihood_ratio.__doc__ += _doc\nnegative_likelihood_ratio.__doc__ += _doc\n\nprecision = positive_predictive_value\nrecall = sensitivity"
  },
  {
    "path": "data_schedule/vis/mapper.py",
    "content": "\nimport json\nimport os\nfrom typing import List\nimport copy\nfrom functools import partial\nimport random\nimport numpy as np\nimport torch\nimport logging\nfrom einops import rearrange\nfrom detectron2.data import MetadataCatalog\n\nfrom data_schedule.registry import MAPPER_REGISTRY\nfrom .mapper_utils import VIS_TrainMapper, VIS_EvalMapper\nfrom .vis_frame_sampler import VIS_FRAMES_SAMPLER_REGISTRY\nfrom data_schedule.vis.apis import VIS_Dataset, VIS_Aug_CallbackAPI,\\\n    VIS_TrainAPI_clipped_video, VIS_EvalAPI_clipped_video_request_ann\n\n@MAPPER_REGISTRY.register()\nclass VIS_Video_EvalMapper(VIS_EvalMapper):\n    def __init__(self,\n                 configs,\n                 dataset_name,\n                 mode,\n                 meta_idx_shift,\n                 ): \n        assert mode == 'evaluate'\n        dataset_meta = MetadataCatalog.get(dataset_name)\n        assert dataset_meta.get('step_size') == None\n        mapper_config = configs['data'][mode][dataset_name]['mapper']\n        super().__init__(meta_idx_shift=meta_idx_shift,\n                         dataset_meta=dataset_meta,\n                         mapper_config=mapper_config)\n\n    def _call(self, data_dict):\n        VIS_Dataset\n        video_id, all_frames = data_dict['video_id'], data_dict['all_frames']\n        video_frames = self.get_frames_fn(video_id=video_id, frames=all_frames)\n        aug_ret = {\n            'video': video_frames,\n            'callback_fns': []\n        }\n        VIS_Aug_CallbackAPI\n        aug_ret = self.augmentation(aug_ret)\n        video = aug_ret.pop('video')\n        callback_fns = aug_ret.pop('callback_fns')[::-1]\n        VIS_EvalAPI_clipped_video_request_ann\n        return {\n            'video_dict': {'video': video},\n            'meta': {\n                'video_id': video_id,\n                'frames': all_frames,\n                'request_ann': torch.ones(len(all_frames)).bool(),\n                'callback_fns': callback_fns               \n            }\n        }\n\n@MAPPER_REGISTRY.register()\nclass VIS_Video_or_Step_To_Clip_TrainMapper(VIS_TrainMapper):\n    def __init__(self,\n                 dataset_name,\n                 configs,\n                 mode, \n                 meta_idx_shift,\n                 ): \n        assert mode == 'train'\n        dataset_meta = MetadataCatalog.get(dataset_name)\n        assert dataset_meta.get('name') == dataset_name\n        mapper_config = configs['data'][mode][dataset_name]['mapper']\n        super().__init__(meta_idx_shift=meta_idx_shift,\n                         dataset_meta=dataset_meta,\n                         mapper_config=mapper_config)\n        \n        self.frames_sampler = VIS_FRAMES_SAMPLER_REGISTRY.get(\\\n            mapper_config['frames_sampler']['name'])(sampler_configs=mapper_config['frames_sampler'],\n                                                    dataset_meta=dataset_meta)\n\n    def _call(self, data_dict):\n        VIS_Dataset\n        video_id, all_frames, all_objs = data_dict['video_id'], data_dict['all_frames'], data_dict['all_objs']\n        frame_idx = data_dict['frame_idx'] if 'frame_idx' in data_dict else None\n\n        all_obj_ids = list(all_objs.keys()) # [1, 2, 5, 4]\n        assert len(list(set(all_obj_ids))) == len(all_obj_ids)\n        class_labels = torch.tensor([all_objs[key]['class_label'] for key in all_obj_ids]) # [8, 10, 20 34]\n\n        re_sample = True\n        sampled_counts = 0\n        while re_sample:\n            sampled_frames = self.frames_sampler(all_frames=all_frames, frame_idx=frame_idx, video_id=video_id)\n            # t' h w, int, obj_ids ;  has_ann t\n            frames_mask, has_ann = self.get_frames_mask_fn(video_id=video_id, frames=sampled_frames)\n            appear_objs = frames_mask.unique() # [0, 1, 2]\n            assert set(appear_objs.tolist()).issubset(set([0] + all_obj_ids))\n            re_sample = (len(list(set(appear_objs.tolist()) & set(all_obj_ids))) == 0)\n            # 只要出现某些个物体就行\n            sampled_counts += 1\n            if sampled_counts > 2:\n                logging.error('sampled two much times')\n                raise RuntimeError()\n            \n        frames_mask = torch.stack([frames_mask == obj_id for obj_id in all_obj_ids], dim=0) # N t' h w, bool\n        video_frames = self.get_frames_fn(video_id=video_id, frames=sampled_frames) \n        width, height = video_frames[0].size\n        aug_ret = {\n            'video': video_frames,\n            'masks': frames_mask, # N t' h w\n            'has_ann': has_ann, # t\n            'classes': class_labels, # N\n        }\n        VIS_Aug_CallbackAPI\n        aug_ret = self.augmentation(aug_ret)\n        video = aug_ret.pop('video')\n        frame_targets = self.map_to_frame_targets(aug_ret)\n        if self.clip_global_targets_map_to_local_targets:\n            aug_ret = self.map_global_targets_to_local_targets(aug_ret)\n\n        VIS_TrainAPI_clipped_video\n        ret = {}\n        ret['video_dict'] = {'video': video}\n        ret['targets'] = aug_ret\n        ret['frame_targets'] = frame_targets\n        return ret\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "data_schedule/vis/mapper_utils.py",
    "content": "from .vis_aug_utils import VIS_EVAL_AUG_REGISTRY, VIS_TRAIN_AUG_REGISTRY\nimport torch\nfrom copy import deepcopy as dcopy\nfrom data_schedule.registry import Mapper\nimport copy\nfrom data_schedule.vis.apis import VIS_TrainAPI_clipped_video\n\nclass VIS_Mapper(Mapper):\n    def __init__(self, \n                 meta_idx_shift,\n                 dataset_meta,) -> None:\n        super().__init__(meta_idx_shift=meta_idx_shift, dataset_meta=dataset_meta)\n        self.get_frames_fn = dataset_meta.get('get_frames_fn')\n\nclass VIS_TrainMapper(VIS_Mapper):\n\n    def __init__(self, \n                 meta_idx_shift, \n                 dataset_meta,\n                 mapper_config) -> None:\n        super().__init__(meta_idx_shift, dataset_meta)\n        self.get_frames_mask_fn = dataset_meta.get('get_frames_mask_fn')   \n        self.clip_global_targets_map_to_local_targets = mapper_config['clip_global_targets_map_to_local_targets'] \n        self.augmentation = VIS_TRAIN_AUG_REGISTRY.get(mapper_config['augmentation']['name'])(mapper_config['augmentation'])\n\n    def map_to_frame_targets(self, clip_targets):\n        VIS_TrainAPI_clipped_video\n        clip_rets = copy.deepcopy(clip_targets)\n        masks = clip_rets['masks'].transpose(0, 1).contiguous() # t' N h w\n        class_labels = clip_rets['classes'] # [10, 32, 10, 4]\n        has_box = 'boxes' in clip_rets\n        if has_box:\n            boxes = clip_rets['boxes'].transpose(0, 1).contiguous() # t' N 4\n            assert len(masks) == len(boxes)\n        ret = []\n        for idx, frame_mk in enumerate(masks):\n            frame_targets = {\n                'masks': frame_mk.unsqueeze(1), # N 1 h w\n                'classes': class_labels, # N\n            }\n            if has_box:\n                frame_targets.update({'boxes': boxes[idx].unsqueeze(1)}) # N 1 4\n            if self.clip_global_targets_map_to_local_targets:\n                frame_targets = self.map_global_targets_to_local_targets(frame_targets)\n            frame_targets['masks'] = frame_targets['masks'].squeeze(1)\n            if has_box:\n                frame_targets['boxes'] = frame_targets['boxes'].squeeze(1)\n            ret.append(frame_targets)\n        return ret\n\n    def map_global_targets_to_local_targets(self, ret):\n        VIS_TrainAPI_clipped_video\n        masks = ret['masks'] # N t' h w\n        global_obj_appear = masks.flatten(1).any(-1) # N [True, False, True, False, False, False, True]\n        ret['masks'] = ret['masks'][global_obj_appear]\n        ret['classes'] = ret['classes'][global_obj_appear]\n        if 'boxes' in ret:\n            ret['boxes'] = ret['boxes'][global_obj_appear] # n t' 4\n        return ret\n    \n\nclass VIS_EvalMapper(VIS_Mapper):\n    def __init__(self, \n                 meta_idx_shift, \n                 dataset_meta,\n                 mapper_config) -> None:\n        super().__init__(meta_idx_shift, dataset_meta)\n        assert mapper_config['augmentation']['name'] in ['WeakPolyP_EvalAug', 'Visha_EvalAug']\n        self.augmentation = VIS_EVAL_AUG_REGISTRY.get(mapper_config['augmentation']['name'])(mapper_config['augmentation'])\n        \n\n\n"
  },
  {
    "path": "data_schedule/vis/polyp/__init__.py",
    "content": "\nfrom . import polyp_dataset\nfrom . import evals\n"
  },
  {
    "path": "data_schedule/vis/polyp/evals.py",
    "content": "from data_schedule.vis.evaluator_utils import register_vis_metric\nimport os\nimport torch\nimport detectron2.utils.comm as comm\nimport logging\nimport subprocess\n\n\n@register_vis_metric\ndef polyp_metric_aggregator(metrics_by_vid_frame, dataset_meta, eval_meta_keys, **kwargs):\n    # output: eval_metrics\n    # video: frame_name: metric/ vid_metrics\n\n    eval_metrics = {}\n    # video, frame_name\n    # perframe metrics\n    metric_names = metrics_by_vid_frame[list(eval_meta_keys.keys())[0]][eval_meta_keys[list(eval_meta_keys.keys())[0]][0]]\n    for taylor_swift in metric_names:\n        eval_metrics[taylor_swift] = torch.tensor([metrics_by_vid_frame[video][frame][taylor_swift]  for video in eval_meta_keys.keys() for frame in eval_meta_keys[video]]).mean()\n    \n    # metrics by each video\n    mean_iou_by_each_video = {}\n    mean_dice_by_each_video = {}\n    for billie_eilish in eval_meta_keys:\n        mean_iou_by_each_video[billie_eilish] = torch.tensor([metrics_by_vid_frame[billie_eilish][fname]['iou'] for fname in eval_meta_keys[billie_eilish]]).mean()\n        mean_dice_by_each_video[billie_eilish] = torch.tensor([metrics_by_vid_frame[billie_eilish][fname]['dice'] for fname in eval_meta_keys[billie_eilish]]).mean()\n        \n    mean_iou_by_each_video = dict(sorted(mean_iou_by_each_video.items(), key=lambda x: x[1]))\n    mean_dice_by_each_video = dict(sorted(mean_dice_by_each_video.items(), key=lambda x: x[1]))    \n    logging.debug(f'mean_iou_by_each_video: {mean_iou_by_each_video}')\n    logging.debug(f'mean_dice_by_each_video: {mean_dice_by_each_video}')\n    \n    return eval_metrics\n\n\n"
  },
  {
    "path": "data_schedule/vis/polyp/polyp_dataset.py",
    "content": "from typing import Optional, Union\nimport json\nimport os\nfrom functools import partial\nimport numpy as np\nimport torch\nimport logging\nfrom tqdm import tqdm\nimport copy\nfrom detectron2.data import DatasetCatalog, MetadataCatalog\nfrom collections import defaultdict\nfrom .polyp_utils import get_frames, get_frames_mask, SET_NAME_TO_DIR, SET_NAME, SET_NAME_TO_NUM_VIDEOS, SET_NAME_TO_MODE, SET_NAME_TO_PREFIX\n\ndef polyp_train(step_size,\n                split_dataset_name,\n                video_ids,\n                video_to_frames,\n                root_path):\n\n    logging.debug(f'{split_dataset_name} Generating metas...')   \n    metas = []\n    for vid_id in tqdm(video_ids):\n        all_frames = sorted(video_to_frames[vid_id])\n        poly_class = 0\n        if step_size is None:  \n            metas.append({\n                'video_id': vid_id,\n                'all_frames' : all_frames,\n                'all_objs': { 1: {'class_label': poly_class} },\n                'meta_idx': len(metas)\n            }) \n        else:\n            for frame_idx in range(0, len(all_frames), step_size):\n                metas.append({\n                    'video_id': vid_id,\n                    'frame_idx': frame_idx,\n                    'all_frames': all_frames,\n                    'all_objs': { 1: {'class_label': poly_class} },\n                    'meta_idx': len(metas)\n                })                \n\n    logging.debug(f'{split_dataset_name} Total metas: [{len(metas)}]')\n    return metas\n\n\ndef polyp_evaluate(eval_video_ids,\n                   split_dataset_name,\n                   step_size,\n                   video_to_frames):\n    if (step_size is not None) and (step_size > 1):\n        logging.warning('why?')\n        raise ValueError()\n    metas = []\n    for video_id in eval_video_ids:\n        all_frames = sorted(video_to_frames[video_id])\n        if step_size == None:\n            metas.append({\n                'video_id': video_id,\n                'all_frames': all_frames,\n                'meta_idx': len(metas)\n            })     \n    \n        else:   \n            for frame_idx in range(0, len(all_frames), step_size):\n                metas.append({\n                    'video_id': video_id,\n                    'frame_idx': frame_idx,\n                    'all_frames': all_frames,\n                    'meta_idx': len(metas)\n                })                                 \n\n    logging.debug(f'{split_dataset_name} Total metas: [{len(metas)}]')  \n    return metas\n\n_root = os.getenv('DATASET_PATH')\n\nroot = os.path.join(_root, 'SUN/SUN-SEG2')\n\nvisualize_meta_idxs = defaultdict(list)\nvisualize_meta_idxs['polyp_train_step[6]'] = [] \nvisualize_meta_idxs['polyp_train'] = [] \nvisualize_meta_idxs['polyp_hard_unseen'] = []\nvisualize_meta_idxs['polyp_hard_seen'] = []\nvisualize_meta_idxs['polyp_easy_unseen'] = []\nvisualize_meta_idxs['polyp_easy_seen'] = []\n\npolyp_meta = {\n    'thing_classes': ['polyp', 'not polyp'],\n    'thing_colors': [(255., 140., 0.), (0., 255., 0.)],\n    'root': root\n}\n\nfor name in SET_NAME:\n    set_dir = SET_NAME_TO_DIR[name]\n    set_dir = os.path.join(root, set_dir)\n    num_videos = SET_NAME_TO_NUM_VIDEOS[name]\n\n    video_ids = os.listdir(os.path.join(set_dir, 'Frame'))\n    assert len(video_ids) == num_videos\n\n    video_to_frames = {\n        vid: sorted([png[:-4] for png in os.listdir(os.path.join(set_dir, 'Frame', vid)) if png.endswith('.jpg')])\\\n            for vid in video_ids\n    }\n    mode = SET_NAME_TO_MODE[name]\n    if mode == 'train':\n        prefix = SET_NAME_TO_PREFIX[name]\n        train_meta = copy.deepcopy(polyp_meta)\n        train_meta.update({\n            'mode': 'train',\n            'get_frames_fn': partial(get_frames, frames_path=os.path.join(set_dir, 'Frame')),\n            'get_frames_mask_fn': partial(get_frames_mask, mask_path=os.path.join(set_dir, 'GT'),),\n        })\n\n        # train\n        for step_size in [1, 3, 6, 9, 12, None]:\n            step_identifer = '' if step_size is None else f'_step[{step_size}]'\n            split_name = f'{prefix}{step_identifer}'\n            train_meta.update({'name': split_name})\n            DatasetCatalog.register(split_name, partial(polyp_train,\n                                                        video_ids=video_ids, \n                                                        split_dataset_name=split_name,\n                                                        step_size=step_size,\n                                                        video_to_frames=video_to_frames,\n                                                        root_path=set_dir))    \n            MetadataCatalog.get(split_name).set(**train_meta, \n                                                step_size=step_size,\n                                                visualize_meta_idxs=visualize_meta_idxs[split_name]) \n    elif mode == 'evaluate':\n        prefix = SET_NAME_TO_PREFIX[name]\n        validate_meta = copy.deepcopy(polyp_meta)\n\n        validate_meta.update({\n            'mode': 'evaluate',\n            'get_frames_fn': partial(get_frames, frames_path=os.path.join(root, os.path.join(set_dir, 'Frame'))),\n            'eval_set_name': SET_NAME_TO_DIR[name],\n            'get_frames_gt_mask_fn': partial(get_frames_mask, mask_path=os.path.join(root, os.path.join(set_dir, 'GT')),),\n            'eval_meta_keys': video_to_frames\n        })\n        for step_size in  [1, None,]:\n            step_identifer = '' if step_size is None else f'_step[{step_size}]'\n            split_name = f'{prefix}{step_identifer}'\n            validate_meta.update({'name': split_name})\n            DatasetCatalog.register(split_name, partial(polyp_evaluate,\n                                                        eval_video_ids=video_ids, \n                                                        split_dataset_name=split_name, \n                                                        step_size=step_size,\n                                                        video_to_frames=video_to_frames))    \n            MetadataCatalog.get(split_name).set(**validate_meta, step_size=step_size,\n                                                visualize_meta_idxs=visualize_meta_idxs[split_name])\n"
  },
  {
    "path": "data_schedule/vis/polyp/polyp_utils.py",
    "content": "\nimport os\nimport numpy as np\nimport torch\nfrom PIL import Image\n\nSET_NAME = ['polyp_train', \n         'polyp_hard_seen_validate', \n         'polyp_hard_unseen_validate', \n         'polyp_easy_seen_validate', \n         'polyp_easy_unseen_validate',\n\n        'polyp_hard_validate',\n        'polyp_easy_validate',\n         'Kvasir-train',\n         'Mayo-train',\n         '300-train',\n         '612-train',\n         '300-tv',\n         '612-test',\n         '612-val'\n         ]\n\nSET_NAME_TO_DIR = {\n    'polyp_train': 'TrainDataset',\n    'polyp_hard_seen_validate': 'TestHardDataset/Seen',\n    'polyp_hard_unseen_validate': 'TestHardDataset/Unseen',\n    'polyp_easy_seen_validate': 'TestEasyDataset/Seen',\n    'polyp_easy_unseen_validate': 'TestEasyDataset/Unseen',\n    \n    'polyp_hard_validate': 'TestHardDataset/Combine',\n    'polyp_easy_validate': 'TestEasyDataset/Combine',\n    \n    'Kvasir-train': 'MICCAI-VPS-dataset/Kvasir-SEG',\n    'Mayo-train': 'MICCAI-VPS-dataset/VPS-TrainSet/ASU-Mayo_Clinic/Train',\n    '300-train': 'MICCAI-VPS-dataset/VPS-TrainSet/CVC-ColonDB-300/Train',\n    '612-train': 'MICCAI-VPS-dataset/VPS-TrainSet/CVC-ClinicDB-612/Train',\n    '300-tv': 'MICCAI-VPS-dataset/VPS-TestSet/CVC-ColonDB-300',\n    '612-test': 'MICCAI-VPS-dataset/VPS-TestSet/CVC-ClinicDB-612-Test',\n    '612-val': 'MICCAI-VPS-dataset/VPS-TestSet/CVC-ClinicDB-612-Valid'\n}\n\nSET_NAME_TO_NUM_VIDEOS = {\n    'polyp_train': 112,\n    'polyp_hard_seen_validate': 17,\n    'polyp_hard_unseen_validate': 37,\n    'polyp_easy_seen_validate': 33,\n    'polyp_easy_unseen_validate': 86,\n    'polyp_hard_validate': 54,\n    'polyp_easy_validate': 119,\n    'Kvasir-train': 1,\n    'Mayo-train': 10,\n    '300-train': 6,\n    '612-train': 18,\n    '300-tv': 6,\n    '612-test': 5,\n    '612-val': 5      \n}\n\nSET_NAME_TO_MODE = {\n    'polyp_train': 'train',\n    'polyp_hard_seen_validate': 'evaluate',\n    'polyp_hard_unseen_validate': 'evaluate',\n    'polyp_easy_seen_validate': 'evaluate',\n    'polyp_easy_unseen_validate': 'evaluate',\n    'polyp_hard_validate': 'evaluate',\n    'polyp_easy_validate': 'evaluate',\n    'Kvasir-train': 'train',\n    'Mayo-train': 'train',\n    '300-train': 'train',\n    '612-train': 'train',\n    '300-tv': 'evaluate',\n    '612-test': 'evaluate',\n    '612-val': 'evaluate'        \n}\n\nSET_NAME_TO_PREFIX = {\n    'polyp_train': 'polyp_train',\n    'polyp_hard_seen_validate': 'polyp_hard_seen_validate',\n    'polyp_hard_unseen_validate': 'polyp_hard_unseen_validate',\n    'polyp_easy_seen_validate': 'polyp_easy_seen_validate',\n    'polyp_easy_unseen_validate': 'polyp_easy_unseen_validate',\n    'polyp_hard_validate': 'polyp_hard_validate',\n    'polyp_easy_validate': 'polyp_easy_validate',\n    'Kvasir-train': 'Kvasir-train',\n    'Mayo-train': 'Mayo-train',\n    '300-train': '300-train',\n    '612-train': '612-train',\n    '300-tv': '300-tv',\n    '612-test': '612-test',\n    '612-val': '612-val'  \n}\n\nCLASS_TO_ID = {\n    'high_grade_adenoma':0, \n    'hyperplastic_polyp':1, \n    'invasive_cancer':2,\n    'low_grade_adenoma':3, \n    'sessile_serrated_lesion':4,\n    'traditional_serrated_adenoma':5\n}\n\ndef get_frames(frames_path, video_id, frames):\n    return [Image.open(os.path.join(frames_path, video_id, f'{f}.jpg')).convert('RGB') for f in frames]\ndef get_frames_mask(mask_path, video_id, frames):\n    # masks = [cv2.imread(os.path.join(mask_path, video_id, f'{f}.jpg')) for f in frames]\n    if os.path.exists(os.path.join(mask_path, video_id, f'{frames[0]}.png')):\n        masks = [Image.open(os.path.join(mask_path, video_id, f'{f}.png')).convert('L') for f in frames]\n    elif os.path.exists(os.path.join(mask_path, video_id, f'{frames[0]}.jpg')):\n        masks = [Image.open(os.path.join(mask_path, video_id, f'{f}.jpg')).convert('L') for f in frames]\n    else:\n        raise ValueError()\n    masks = [np.array(mk) for mk in masks]\n    masks = torch.stack([torch.from_numpy(mk) for mk in masks], dim=0) # t h w\n    # assert set(masks.unique().tolist()) == set([0, 255]), f'{masks.unique().tolist()}'\n    masks = (masks > 0).int()\n    return masks, torch.ones(len(frames)).bool()\n\n\n"
  },
  {
    "path": "data_schedule/vis/vis_aug_eval.py",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\nimport random\nimport torch\nimport torchvision.transforms.functional as F\nfrom data_schedule.vis.apis import VIS_Aug_CallbackAPI\nfrom .vis_aug_utils import  get_tgt_size\n\nfrom .vis_aug_utils import VIS_EVAL_AUG_REGISTRY\n\nclass RandomResize:\n    def __init__(self, sizes, max_size=None):\n        assert isinstance(sizes, (list, tuple))\n        self.sizes = sizes\n        self.max_size = max_size\n\n    def __call__(self, ret):\n        video = ret['video']\n        orig_size = video[0].size # w h\n        tgt_size = get_tgt_size(video[0].size, random.choice(self.sizes), self.max_size) # h w\n\n        resized_video = [F.resize(frame, tgt_size) for frame in video]\n        ratio_width, ratio_height = tuple(float(s) / float(s_orig) for s, s_orig in zip(tgt_size[::-1], orig_size))\n        ret['video'] = resized_video\n\n        if 'callback_fns' in ret:\n            VIS_Aug_CallbackAPI\n            ret['callback_fns'].append(RandomResize(sizes=[orig_size], max_size=None))\n\n        if \"pred_masks\" in ret:\n            assert (len(self.sizes) == 1) and (self.max_size == None)\n            VIS_Aug_CallbackAPI\n            pred_masks = ret['pred_masks'] # list[nt h w], t\n            pred_masks = [torch.nn.functional.interpolate(mk.unsqueeze(0).float(), tgt_size, mode='nearest')[0].bool()\n                          for mk in pred_masks]\n            ret['pred_masks'] = pred_masks # list[nt h w], t\n\n        if \"pred_boxes\" in ret:\n            VIS_Aug_CallbackAPI\n            pred_boxes = ret[\"pred_boxes\"] # list[nt 4], t\n            scaled_boxes = [bx * (torch.tensor([ratio_width, ratio_height, ratio_width, ratio_height])[None, :])\n                            for bx in pred_boxes]\n            ret[\"pred_boxes\"] = scaled_boxes\n\n        return ret\n    \nclass VideoToPIL:\n    def __call__(self, ret):\n        video = ret['video'] # t 3 h w ->\n        assert video.dtype == torch.float and (video.max() <= 1) and (video.min() >=0)  \n        pil_video = [F.to_pil_image(frame, mode='RGB') for frame in video] # 3 h w, float, 0-1\n        ret['video'] = pil_video\n        assert 'callback_fns' not in ret\n        return ret\n\n\nclass VideoToTensor:\n    def __call__(self, ret):\n        video = ret['video']\n        tensor_video = torch.stack([F.to_tensor(frame) for frame in video], dim=0) # t 3 h w, float, 0-1\n        ret['video'] = tensor_video\n\n        if 'callback_fns' in ret:\n            VIS_Aug_CallbackAPI\n            ret['callback_fns'].append(VideoToPIL())\n\n        return ret\n        \n\n@VIS_EVAL_AUG_REGISTRY.register()\nclass WeakPolyP_EvalAug:\n    def __init__(self, configs) -> None:\n        self.resize = RandomResize(\n            sizes=[[352, 352]],\n        )\n        self.tensor_video = VideoToTensor()\n\n    def __call__(self, ret):\n        VIS_Aug_CallbackAPI\n        ret = self.resize(ret)\n        ret = self.tensor_video(ret)        \n        return ret\n"
  },
  {
    "path": "data_schedule/vis/vis_aug_train.py",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\nimport random\nfrom PIL import Image\nimport torch\nimport torchvision.transforms.functional as F\nfrom einops import rearrange\nfrom copy import deepcopy as dcopy\nfrom data_schedule.vis.apis import VIS_Aug_CallbackAPI\nimport albumentations as A\nimport numpy as np\nfrom data_schedule.utils.segmentation import bounding_box_from_mask\nfrom .vis_aug_utils import VIS_TRAIN_AUG_REGISTRY, pil_torch_to_numpy, numpy_to_pil_torch\n\nimport copy\n    \nimport imgaug.augmenters as iaa\nfrom imgaug.augmentables.segmaps import SegmentationMapsOnImage\nfrom imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage\nimport imgaug\nfrom datetime import datetime\n\nclass RandomRotate90:\n    def __init__(self) -> None:\n        self.album_aug = A.ReplayCompose(\n            [A.RandomRotate90(0.5)]\n        )\n    \n    def __call__(self, ret):\n        video = ret['video'] \n        masks = ret['masks'] \n        has_ann = ret['has_ann']\n        # list[PIL], n t' h w -> \n        # list[h w 3, 255rgb], t\n        # list[list[h w, 01uint8]] t\n        video, masks = pil_torch_to_numpy(video=video, masks=masks, has_ann=has_ann)\n        replay = self.album_aug(image=video[0], mask=[masks[0][0]])['replay']\n        auged_video = []\n        auged_mask = []\n        for vid, mk in zip(video, masks):\n            ret = self.album_aug.replay(replay, image=vid, mask=mk)\n            auged_video.append(ret['image'])\n            auged_mask.append(ret['mask'])\n        \n        auged_video, auged_mask = numpy_to_pil_torch(video=auged_video, auged_mask=auged_mask, has_ann=has_ann)\n\n        ret['video'] = auged_video\n        ret['mask'] = auged_mask\n\n        return ret\n\nclass ComputeBox:\n    def __call__(self, ret):\n        W, H = ret['video'][0].size\n        N, T = ret['masks'].shape[:2] # n t' h w\n        boxes = torch.stack([bounding_box_from_mask(mask) for mask in copy.deepcopy(ret['masks']).flatten(0, 1)], dim=0) # Nt' 4\n        boxes = rearrange(boxes, '(N T) c -> N T c', N=N, T=T)\n        boxes[:, :, 0::2].clamp_(min=0, max=W)\n        boxes[:, :, 1::2].clamp_(min=0, max=H)\n\n        ret['boxes'] = boxes\n\n        return ret\n\nclass VideoToTensor:\n    def __call__(self, ret):\n        video = ret['video']\n        tensor_video = torch.stack([F.to_tensor(frame) for frame in video], dim=0) # t 3 h w, float, 0-1\n        ret['video'] = tensor_video\n        return ret\n\nclass Compose:\n    def __init__(self, transforms):\n        self.transforms = transforms\n\n    def __call__(self, ret):\n        for t in self.transforms:\n            ret = t(ret)\n        return ret\n\n    def __repr__(self):\n        format_string = self.__class__.__name__ + \"(\"\n        for t in self.transforms:\n            format_string += \"\\n\"\n            format_string += \"    {0}\".format(t)\n        format_string += \"\\n)\"\n        return format_string\n\n\n@VIS_TRAIN_AUG_REGISTRY.register()\nclass WeakPolyP_TrainAug:\n    def __init__(self, configs) -> None:\n        self.transform = A.ReplayCompose([\n            A.Resize(352, 352),\n            A.HorizontalFlip(p=0.5),\n            A.VerticalFlip(p=0.5),\n            A.RandomRotate90(p=0.5),\n        ])\n\n        self.tensor_video = VideoToTensor()\n        self.add_box = ComputeBox()\n\n    def __call__(self, ret):\n        VIS_Aug_CallbackAPI\n        video = ret['video'] \n        masks = ret['masks']  # n t' h w\n        has_ann = ret['has_ann'] # t\n        # list[PIL] -> list[h w 3, 0-1float], t\n        # n t' h w -> list[list[h w, 01uint8], 没有annotation的帧box是空] t\n        video, masks = pil_torch_to_numpy(video=video, masks=masks, has_ann=has_ann)\n\n        replay = self.transform(image=video[0], masks=[masks[0][0]])['replay']\n        auged_video = []\n        auged_mask = []\n        for vid, mk in zip(video, masks):\n            auged_each_frame = self.transform.replay(replay, image=vid, masks=mk)\n            auged_video.append(auged_each_frame['image'])\n            auged_mask.append(auged_each_frame['masks']) # list[h w, 01uint8]\n        \n        auged_video, auged_mask = numpy_to_pil_torch(video=auged_video, masks=auged_mask, has_ann=has_ann)\n\n        ret['video'] = auged_video\n        ret['masks'] = auged_mask\n        \n        ret = self.add_box(ret)\n        ret = self.tensor_video(ret)\n\n        return ret\n\n\n@VIS_TRAIN_AUG_REGISTRY.register()\nclass WeakPolyP_TrainAug_RotateImageToClip:\n    def __init__(self, configs) -> None:\n        self.ImageToSeqAugmenter = ImageToSeqAugmenter(perspective=True, affine=True, motion_blur=True,\n                                    rotation_range=(-20, 20), perspective_magnitude=0.08,\n                                    hue_saturation_range=(-5, 5), brightness_range=(-40, 40),\n                                    motion_blur_prob=0.25, motion_blur_kernel_sizes=(9, 11),\n                                    translate_range=(-0.1, 0.1))\n        self.num_frames = configs['num_frames']\n        \n        self.transform = A.ReplayCompose([\n            A.Resize(352, 352),\n            A.HorizontalFlip(p=0.5),\n            A.VerticalFlip(p=0.5),\n            A.RandomRotate90(p=0.5),\n        ])\n        self.tensor_video = VideoToTensor()\n        self.add_box = ComputeBox()\n\n    def apply_random_sequence_shuffle(self, images, instance_masks):\n        perm = list(range(self.num_frames))\n        random.shuffle(perm)\n        images = [images[i] for i in perm]\n        instance_masks = [instance_masks[i] for i in perm]\n        return images, instance_masks\n    \n    def __call__(self, ret):\n        VIS_Aug_CallbackAPI\n        video = ret['video']  # list[pil], t\n        masks = ret['masks']  # n t' h w\n        has_ann = ret['has_ann'] # t\n\n        # list[PIL] -> list[h w 3, uint8], t\n        # n t' h w -> list[list[h w], n, uint8], t\n        seq_images, seq_instance_masks = pil_torch_to_numpy(video=video, masks=masks, has_ann=has_ann, float_image=False)\n        assert len(seq_images) == 1 and len(seq_instance_masks) == 1\n        static_img, static_mask = seq_images[0], seq_instance_masks[0]\n        for t in range(self.num_frames - 1):\n            im_trafo, instance_masks_trafo = self.ImageToSeqAugmenter(static_img, static_mask) # h w 3, uint8; list[h w], n, uint8\n            seq_images.append(np.uint8(im_trafo))\n            seq_instance_masks.append(instance_masks_trafo)\n        # list[h w 3], t ;  # list[list[h w, 01uint8]] t \n        seq_images, seq_instance_masks = self.apply_random_sequence_shuffle(seq_images, seq_instance_masks)         \n        has_ann = torch.ones(self.num_frames).bool() # T\n        seq_images = [np.float32(haosen) / 255.0 for haosen in seq_images] # list[h w 3, 0-1float], t\n        replay = self.transform(image=seq_images[0], masks=[seq_instance_masks[0][0]])['replay']\n        auged_video = []\n        auged_mask = []\n        for vid, mk in zip(seq_images, seq_instance_masks):\n            auged_each_frame = self.transform.replay(replay, image=vid, masks=mk)\n            auged_video.append(auged_each_frame['image'])\n            auged_mask.append(auged_each_frame['masks']) # list[h w, 01uint8]\n        \n        auged_video, auged_mask = numpy_to_pil_torch(video=auged_video, masks=auged_mask, has_ann=has_ann) # n t h w\n        # [haosen.save(f'./test{idx}.png') for idx, haosen in enumerate(auged_video)]\n        # import matplotlib.pyplot as plt\n        # [plt.imsave( f'./mask{idx}.png', auged_mask[0][idx].float().numpy()) for idx in range(len(auged_mask[0]))]\n        ret['video'] = auged_video\n        ret['masks'] = auged_mask\n        ret['has_ann'] = has_ann\n        \n        ret = self.add_box(ret)\n        ret = self.tensor_video(ret)\n\n        return ret\n    \n\nclass ImageToSeqAugmenter(object):\n    def __init__(self, perspective=True, affine=True, motion_blur=True,\n                 brightness_range=(-50, 50), hue_saturation_range=(-15, 15), perspective_magnitude=0.12,\n                 scale_range=1.0, translate_range={\"x\": (-0.15, 0.15), \"y\": (-0.15, 0.15)}, rotation_range=(-20, 20),\n                 motion_blur_kernel_sizes=(7, 9), motion_blur_prob=0.5, seed=2024):\n\n        self.basic_augmenter = iaa.SomeOf((1, None), [\n                iaa.Add(brightness_range),\n                iaa.AddToHueAndSaturation(hue_saturation_range)\n            ]\n        )\n\n        transforms = []\n        if perspective:\n            transforms.append(iaa.PerspectiveTransform(perspective_magnitude))\n        if affine:\n            transforms.append(iaa.Affine(scale=scale_range,\n                                         translate_percent=translate_range,\n                                         rotate=rotation_range,\n                                         order=1,  # cv2.INTER_LINEAR\n                                         backend='auto'))\n        transforms = iaa.Sequential(transforms)\n        transforms = [transforms]\n\n        if motion_blur:\n            blur = iaa.Sometimes(motion_blur_prob, iaa.OneOf(\n                [\n                    iaa.MotionBlur(ksize)\n                    for ksize in motion_blur_kernel_sizes\n                ]\n            ))\n            transforms.append(blur)\n\n        self.frame_shift_augmenter = iaa.Sequential(transforms)\n        self.seed = seed\n    @staticmethod\n    def condense_masks(instance_masks):\n        condensed_mask = np.zeros_like(instance_masks[0], dtype=np.int8)\n        for instance_id, mask in enumerate(instance_masks, 1):\n            condensed_mask = np.where(mask, instance_id, condensed_mask)\n\n        return condensed_mask\n\n    @staticmethod\n    def expand_masks(condensed_mask, num_instances):\n        return [(condensed_mask == instance_id).astype(np.uint8) for instance_id in range(1, num_instances + 1)]\n\n    def __call__(self, image, masks=None, boxes=None): # n h w\n        det_augmenter = self.frame_shift_augmenter.to_deterministic()\n        if masks is not None:\n            masks_np, is_binary_mask = [], []\n            boxs_np = []\n\n            for mask in masks:\n                \n                if isinstance(mask, np.ndarray):\n                    masks_np.append(mask.astype(np.bool_))\n                    is_binary_mask.append(False)\n                else:\n                    raise ValueError(\"Invalid mask type: {}\".format(type(mask)))\n\n            num_instances = len(masks_np)\n            masks_np = SegmentationMapsOnImage(self.condense_masks(masks_np), shape=image.shape[:2])\n            # boxs_np = BoundingBoxesOnImage(boxs_np, shape=image.shape[:2])\n            seed = int(datetime.now().strftime('%M%S%f')[-8:])\n            imgaug.seed(seed)\n            aug_image, aug_masks = det_augmenter(image=self.basic_augmenter(image=image) , segmentation_maps=masks_np)\n            imgaug.seed(seed)\n            invalid_pts_mask = det_augmenter(image=np.ones(image.shape[:2] + (1,), np.uint8)).squeeze(2)\n            aug_masks = self.expand_masks(aug_masks.get_arr(), num_instances)\n            # aug_boxes = aug_boxes.remove_out_of_image().clip_out_of_image()\n            aug_masks = [mask for mask, is_bm in zip(aug_masks, is_binary_mask)]\n            return aug_image, aug_masks #, aug_boxes.to_xyxy_array()\n\n        else:\n            masks = [SegmentationMapsOnImage(np.ones(image.shape[:2], np.bool), shape=image.shape[:2])]\n            aug_image, invalid_pts_mask = det_augmenter(image=image, segmentation_maps=masks)\n            return aug_image, invalid_pts_mask.get_arr() == 0\n\n"
  },
  {
    "path": "data_schedule/vis/vis_aug_utils.py",
    "content": "from detectron2.utils.registry import Registry\nimport torch\nimport numpy as np\nimport torchvision.transforms.functional as F\nfrom PIL import Image\nfrom einops import rearrange, reduce, repeat\nVIS_EVAL_AUG_REGISTRY = Registry('VIS_EVAL_AUG')\nVIS_TRAIN_AUG_REGISTRY = Registry('VIS_TRAIN_AUG')\n\n\ndef get_size_with_aspect_ratio(image_size, size, max_size=None):\n    w, h = image_size\n    if max_size is not None:\n        min_original_size = float(min((w, h)))\n        max_original_size = float(max((w, h)))\n        if max_original_size / min_original_size * size > max_size:\n            size = int(round(max_size * min_original_size / max_original_size))\n\n    if (w <= h and w == size) or (h <= w and h == size):\n        return (h, w)\n\n    if w < h:\n        ow = size\n        oh = int(size * h / w)\n    else:\n        oh = size\n        ow = int(size * w / h)\n\n    return (oh, ow)\n\ndef get_tgt_size(image_size, size, max_size=None):\n    if isinstance(size, (list, tuple)):\n        return size[::-1]\n    else:\n        return get_size_with_aspect_ratio(image_size, size, max_size)\n\ndef pil_torch_to_numpy(video, masks, has_ann, float_image=True):\n    # n t' h w\n    # list[pil_image, rgb], t\n    # t\n    N, T = masks.shape[:2]\n    has_ann_idx = torch.where(has_ann)[0] # time_idx\n    # list[Image], t -> list[h w 3, 255uint8], t\n    masks = masks.permute(1, 0, 2, 3).contiguous().unbind(0) # list[n h w] t'\n    numpy_masks = [[]] * len(has_ann) # list[list[h w, 01_uint8], n], t\n    assert len(has_ann_idx) == len(masks)\n    for fmask, taylor in zip(masks, has_ann_idx): # n h w\n        fnumpy_masks = []\n        for mk in fmask.unbind(0): # h w\n            fnumpy_masks.append(mk.numpy().astype(np.uint8))\n        numpy_masks[taylor] = fnumpy_masks\n    \n    if float_image:\n        # list[h w 3, 0-1float], t\n        video = [F.to_tensor(frame).permute(1,2,0).numpy() for frame in video]\n    else:\n        # uint8\n        video = [np.array(frame) for frame in video]\n    \n    return video, numpy_masks\n\ndef numpy_to_pil_torch(video, masks, has_ann):\n    # numpy, numpy -> torch, torch\n    # list[h w 3, 0-1float], t\n    H, W = video[0].shape[:2]\n    T = has_ann.int().sum()\n    video = [Image.fromarray(np.uint8(aug_vid * 255), mode=\"RGB\")  for aug_vid in video]\n    # t'n h w\n    torch_masks = torch.stack([torch.from_numpy(obj_mk).bool() for frame_mk in masks for obj_mk in frame_mk], dim=0)\n    torch_masks = rearrange(torch_masks, '(T N) h w -> N T h w', T=T) # n t' h w\n\n    return video, torch_masks \n\n\n\n\n\n\n\n"
  },
  {
    "path": "data_schedule/vis/vis_frame_sampler.py",
    "content": "\nfrom detectron2.utils.registry import Registry\nimport random\nimport numpy as np\nimport torch\nimport logging\nfrom detectron2.utils import comm\n\nVIS_FRAMES_SAMPLER_REGISTRY = Registry('VIS_FRAMES_SAMPLER')\n\nimport random\n\n@VIS_FRAMES_SAMPLER_REGISTRY.register()\nclass Naive_ReferenceFrame_FrameSampler:\n    def __init__(self, sampler_configs, dataset_meta, **kwargs):            \n        self.reference_frame_step_size = dataset_meta.get('step_size')\n\n        self.clip_sizes = list(sampler_configs['clip_sizes']) # list[int]\n        self.clip_distribute = sampler_configs['clip_distribute'] # dense, sparse, local_global\n        self.clip_position = sampler_configs['clip_position'] # former, center, latter\n\n        if max(self.clip_sizes) > self.reference_frame_step_size:\n            if comm.is_main_process():\n                logging.warning('')\n\n    def __call__(self, \n                 frame_idx=None,\n                 all_frames=None, # list[str]\n                 **kwargs):\n        random_clip_size = random.choice(self.clip_sizes)\n        video_len = len(all_frames)\n        sample_indx = [frame_idx]\n        if (self.clip_position == 'center') and (self.clip_distribute == 'local_global'):\n            if random_clip_size != 1:\n                sample_id_before = random.randint(1, 3)\n                sample_id_after = random.randint(1, 3)\n                local_indx = [max(0, frame_idx - sample_id_before), min(video_len - 1, frame_idx + sample_id_after)]\n                sample_indx.extend(local_indx)\n                if random_clip_size > 3:\n                    all_inds = list(range(video_len))\n                    global_inds = all_inds[:min(sample_indx)] + all_inds[max(sample_indx):]\n                    global_n = random_clip_size - len(sample_indx)\n                    if len(global_inds) > global_n:\n                        select_id = random.sample(range(len(global_inds)), global_n)\n                        for s_id in select_id:\n                            sample_indx.append(global_inds[s_id])\n                    elif video_len >= global_n: \n                        select_id = random.sample(range(video_len), global_n)\n                        for s_id in select_id:\n                            sample_indx.append(all_inds[s_id])\n                    else:\n                        select_id = random.sample(range(video_len), global_n - video_len) + list(range(video_len))           \n                        for s_id in select_id:                                                                   \n                            sample_indx.append(all_inds[s_id])\n        \n        elif (self.clip_position == 'center') and (self.clip_distribute == 'dense'):\n            half_size = (random_clip_size - 1) // 2\n            sample_indx += list(range(frame_idx - half_size, frame_idx))\n            sample_indx += list(range(frame_idx+1, half_size + frame_idx + 1))\n\n            if len(sample_indx) < random_clip_size: \n                sample_indx = [min(sample_indx)] + sample_indx\n            assert len(sample_indx) == random_clip_size\n            sample_indx = torch.tensor(sample_indx)\n            sample_indx = sample_indx.clamp_(min=0, max=video_len-1)\n            sample_indx = sample_indx.tolist()\n        else:\n            raise ValueError()\n        sample_indx.sort()\n        sampled_frames = [all_frames[idx] for idx in sample_indx]\n        return sampled_frames\n\n"
  },
  {
    "path": "handle_vps.py",
    "content": "\nimport cv2\nimport numpy as np\nimport os\nimport shutil\nfrom PIL import Image\nimport torch\nfrom tqdm import tqdm\ndataset_root = os.getenv('DATASET_PATH')\n# the original IVPS is the union of Kvasir and per-frame Mayo/CVC\nall_images = os.listdir(f'{dataset_root}/MICCAI-VPS-dataset/IVPS-TrainSet/Frame')\nka_images = [b for b in all_images if b.startswith('K')]\nassert len(ka_images) == 1000\n\nall_gts = os.listdir(f'{dataset_root}/MICCAI-VPS-dataset/IVPS-TrainSet/GT')\nka_gts = [b for b in all_gts if b.startswith('K')]\nassert len(ka_gts) == 1000\n\nos.makedirs(os.path.join(f'{dataset_root}/MICCAI-VPS-dataset/Kvasir-SEG/Frame/1'),exist_ok=True)\nos.makedirs(os.path.join(f'{dataset_root}/MICCAI-VPS-dataset/Kvasir-SEG/GT/1'),exist_ok=True)\n\nfor image_id in tqdm(ka_images):\n    shutil.copy(os.path.join(f'{dataset_root}/MICCAI-VPS-dataset/IVPS-TrainSet/Frame', f'{image_id}'),\n                os.path.join(f'{dataset_root}/MICCAI-VPS-dataset/Kvasir-SEG/Frame/1', f'{image_id}'),)\n    \nfor image_id in tqdm(ka_gts):\n    shutil.copy(os.path.join(f'{dataset_root}/MICCAI-VPS-dataset/IVPS-TrainSet/GT', f'{image_id}'),\n                os.path.join(f'{dataset_root}/MICCAI-VPS-dataset/Kvasir-SEG/GT/1', f'{image_id}'),)   \n\n# normalize train directory\nfor base_path in [f'{dataset_root}/MICCAI-VPS-dataset/VPS-TrainSet/CVC-ColonDB-300/Train',\n                  f'{dataset_root}/MICCAI-VPS-dataset/VPS-TrainSet/ASU-Mayo_Clinic/Train',\n                  f'{dataset_root}/MICCAI-VPS-dataset/VPS-TrainSet/CVC-ClinicDB-612/Train']:\n    video_ids = os.listdir(base_path)\n    frame_path = os.path.join(base_path, 'Frame')\n    gt_path = os.path.join(base_path, 'GT')\n    os.makedirs(frame_path, exist_ok=True)\n    os.makedirs(gt_path, exist_ok=True)\n    # Iterate through each video ID directory\n    for vid in video_ids:\n        shutil.copytree(os.path.join(base_path, vid, 'Frame'), os.path.join(frame_path, vid))\n        shutil.copytree(os.path.join(base_path, vid, 'GT'), os.path.join(gt_path, vid))\n        # TODO: dangerous: remove if you want\n\n# remove non-mask frames of each training set\nSET_NAME = [\n    'Kvasir-train',\n    'Mayo-train',\n    '300-train',\n    '612-train',\n]\n\nSET_NAME_TO_DIR = {\n    'Kvasir-train': 'MICCAI-VPS-dataset/Kvasir-SEG',\n    'Mayo-train': 'MICCAI-VPS-dataset/VPS-TrainSet/ASU-Mayo_Clinic/Train',\n    '300-train': 'MICCAI-VPS-dataset/VPS-TrainSet/CVC-ColonDB-300/Train',\n    '612-train': 'MICCAI-VPS-dataset/VPS-TrainSet/CVC-ClinicDB-612/Train',\n}\n\nSET_NAME_TO_NUM_VIDEOS = {\n    'Kvasir-train': 1,\n    'Mayo-train': 10,\n    '300-train': 6,\n    '612-train': 18,\n    '300-tv': 6,\n    '612-test': 5,\n    '612-val': 5      \n}\n\n\nSET_NAME_TO_PREFIX = {\n    'Kvasir-train': 'Kvasir-train',\n    'Mayo-train': 'Mayo-train',\n    '300-train': '300-train',\n    '612-train': '612-train',\n}\n\n\nroot = os.getenv('DATASET_PATH')\ndef get_frames_mask(mask_path, video_id, frames):\n    # masks = [cv2.imread(os.path.join(mask_path, video_id, f'{f}.jpg')) for f in frames]\n    if os.path.exists(os.path.join(mask_path, video_id, f'{frames[0]}.png')):\n        masks = [Image.open(os.path.join(mask_path, video_id, f'{f}.png')).convert('L') for f in frames]\n    elif os.path.exists(os.path.join(mask_path, video_id, f'{frames[0]}.jpg')):\n        masks = [Image.open(os.path.join(mask_path, video_id, f'{f}.jpg')).convert('L') for f in frames]\n    else:\n        raise ValueError()\n    masks = [np.array(mk) for mk in masks]\n    masks = torch.stack([torch.from_numpy(mk) for mk in masks], dim=0) # t h w\n    # assert set(masks.unique().tolist()) == set([0, 255]), f'{masks.unique().tolist()}'\n    masks = (masks > 0).int()\n    return masks, torch.ones(len(frames)).bool()\n    \nnum_delted_frames = 0\nfor train_set_name in SET_NAME:\n    set_dir = SET_NAME_TO_DIR[train_set_name]\n    frames_dir = os.path.join(root, set_dir, 'Frame')\n    mask_dir = os.path.join(root, set_dir, 'GT')\n\n    video_ids = os.listdir(frames_dir)\n    for vid in tqdm(video_ids):\n        frames = [haosen[:-4] for haosen in os.listdir(os.path.join(frames_dir, vid))]\n        frame_has_fore = [get_frames_mask(mask_dir, vid, [haosen])[0].any() for haosen in tqdm(frames)] # list[t]\n        assert len(frame_has_fore) == len(frames)\n        num_delted_frames += (~ torch.tensor(frame_has_fore)).int().sum()\n        for haosen, frame_name in tqdm(zip(frame_has_fore, frames)):\n            if not haosen:\n                os.remove(os.path.join(frames_dir, vid, f'{frame_name}.jpg'))\n\n                if os.path.exists(os.path.join(mask_dir, vid, f'{frame_name}.jpg')):\n                    os.remove(os.path.join(mask_dir, vid, f'{frame_name}.jpg'))\n                elif os.path.exists(os.path.join(mask_dir, vid, f'{frame_name}.png')):\n                    os.remove(os.path.join(mask_dir, vid, f'{frame_name}.png')) \n                else:\n                    raise ValueError()\n\nprint(f'should be {num_delted_frames}/1546.') # should be 1546\n\n\n\n\n"
  },
  {
    "path": "main.py",
    "content": "import os\nimport argparse\nimport logging\nimport importlib\nfrom trainers import task_to_trainer\nimport detectron2.utils.comm as comm\nfrom termcolor import colored\nimport logging\nimport yaml\nimport torch\nfrom utils.misc import setup_for_distributed\n\ndef _highlight(code, filename):\n    try:\n        import pygments\n    except ImportError:\n        return code\n\n    from pygments.lexers import Python3Lexer, YamlLexer\n    from pygments.formatters import Terminal256Formatter\n\n    lexer = Python3Lexer() if filename.endswith(\".py\") else YamlLexer()\n    code = pygments.highlight(code, lexer, Terminal256Formatter(style=\"monokai\"))\n    return code\n\nclass _ColorfulFormatter(logging.Formatter):\n    def __init__(self, *args, **kwargs):\n        self._root_name = kwargs.pop(\"root_name\") + \".\"\n        self._abbrev_name = kwargs.pop(\"abbrev_name\", \"\")\n        if len(self._abbrev_name):\n            self._abbrev_name = self._abbrev_name + \".\"\n        super(_ColorfulFormatter, self).__init__(*args, **kwargs)\n    def formatMessage(self, record):\n        record.name = record.name.replace(self._root_name, self._abbrev_name)\n        message = record.message\n        # message, asctime, name, filename = record.message, record.asctime, record.name, record.filename\n        log = super(_ColorfulFormatter, self).formatMessage(record)\n        if (record.levelno == logging.WARNING) or (record.levelno == logging.ERROR) or (record.levelno == logging.CRITICAL):\n            colored_message = colored(message, \"red\", attrs=[\"blink\", \"underline\"])\n        elif record.levelno == logging.DEBUG:\n            colored_message = colored(message, \"yellow\", attrs=[\"blink\", \"underline\"])\n        else: # INFO/NOTSET\n            colored_message = colored(message, \"white\")  \n        return log + colored_message\n\ndef set_logging_file(output_dir, file_name, mode='a'):\n    handler1 = logging.StreamHandler()\n    handler2 = logging.FileHandler(os.path.join(output_dir, file_name), mode=mode)\n    formatter = _ColorfulFormatter(\n        colored(\"[%(asctime)s %(name)s %(filename)s]: \", \"green\"),\n        datefmt=\"%m/%d %H:%M:%S\",\n        root_name=os.path.join(output_dir, file_name),\n        abbrev_name=str('grey'),\n    )\n    handler1.setFormatter(formatter)\n    handler2.setFormatter(formatter)\n    logger = logging.getLogger()\n    logger.addHandler(handler1)\n    logger.addHandler(handler2)\n    logger.setLevel(logging.DEBUG)\n\n\ndef init_process_group_and_set_device(world_size, process_id, device_id):\n    \"\"\"\n    This function needs to be called on each spawned process to initiate learning using DistributedDataParallel.\n    The function initiates the process' process group and assigns it a single GPU to use during training.\n    \"\"\"\n    torch.cuda.set_device(device_id)\n    device = torch.device(f'cuda:{device_id}')\n    if world_size > 1:\n        torch.distributed.init_process_group(\n            torch.distributed.Backend.NCCL,\n            world_size=world_size,\n            rank=process_id\n        )\n        comm.create_local_process_group(world_size)\n        torch.distributed.barrier(device_ids=[device_id])\n        setup_for_distributed(process_id == 0)\n    return device\n\ndef run(rank, configs, world_size):\n    os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'max_split_size_mb:512'\n    os.environ[\"TOKENIZERS_PARALLELISM\"] = \"false\"\n    os.environ['PYDEVD_WARN_SLOW_RESOLVE_TIMEOUT'] = \"4\"\n    os.environ['PYDEVD_DISABLE_FILE_VALIDATION'] = \"1\"\n    os.environ[\"DGLBACKEND\"] = \"pytorch\"\n    logging.getLogger('penman').setLevel(logging.WARNING)    \n    logging.getLogger('PIL').setLevel(logging.WARNING) \n    logging.getLogger('PIL.PngImagePlugin').setLevel(logging.WARNING)   \n    logging.getLogger('matplotlib').setLevel(logging.WARNING)\n    logging.getLogger('urllib3').setLevel(logging.WARNING)\n    logging.getLogger('h5py').setLevel(logging.WARNING)\n    init_process_group_and_set_device(world_size, process_id=rank, device_id=rank)\n    if comm.is_main_process():\n        mode = configs['trainer_mode']\n        out_dir = configs['out_dir']\n        if mode == 'eval':\n            num_of_eval_times = len([eval_txt for eval_txt in os.listdir(out_dir) if eval_txt.endswith('eval.txt')])\n            set_logging_file(out_dir, f\"eval.txt\", mode='a')\n            path = os.path.join(out_dir, f\"config_eval.yaml\")\n        else:\n            num_of_train_times = len([train_txt for train_txt in os.listdir(out_dir) if train_txt.endswith('train.txt')])\n            if 'resume' in mode:\n                set_logging_file(out_dir, f\"train.txt\", mode='a')\n            else:\n                set_logging_file(out_dir, f\"train.txt\", mode='w')\n            path = os.path.join(out_dir, f\"config_train.yaml\")\n            \n        logging.debug(\"Running with full config:\\n{}\".format(_highlight(yaml.dump(configs, default_flow_style=False), \".yaml\")))\n        with open(path, \"w\") as f:\n            f.write(yaml.dump(configs, default_flow_style=False))\n        logging.debug(\"Full config saved to {}\".format(path)) \n    comm.synchronize()\n    trainer = task_to_trainer[configs['task']](configs=configs)\n    comm.synchronize()\n    if configs['trainer_mode'] == 'eval':\n        eval_ckpts = configs['eval_ckpts']\n        for lunch in eval_ckpts:\n            trainer.load_ckpt(lunch, load_model=True, load_schedule=True, load_random=False, load_optimize=False)\n            trainer.evaluate()\n\n    else:\n        if configs['trainer_mode'] == 'train_resume':\n            ckpt_dirs = os.listdir(configs['out_dir'])\n            ckpt_dirs = sorted([a for a in ckpt_dirs if a.startswith('epc')], key=lambda x:int(x.split('sap[')[-1][:-1]))\n            trainer_ckpt = '/'.join([configs['out_dir'], ckpt_dirs[-1], 'ckpt.pth.tar'])\n            trainer.load_ckpt(trainer_ckpt, load_model=True, load_schedule=True, load_random=True, load_optimize=True)\n        trainer.train()\n\nif __name__==\"__main__\":\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('--config_file', type=str, required=True)\n    parser.add_argument('--trainer_mode', type=str, default='train_attmpt')  \n    parser.add_argument('--eval_path', type=str, default='') \n    args = parser.parse_args()\n    task, group, config, config2 = args.config_file.split('/')[-4:]\n    assert config == config2[:-3]\n    config_file = '.'.join(['output', task, group, config, config])\n    configs = importlib.import_module(config_file).trainer_configs\n    configs['task'], configs['group'], configs['config'] = task, group, config\n    configs['out_dir'] = os.path.join('./', 'output', task, group, config)\n    configs['trainer_mode'] = args.trainer_mode\n\n    if configs['trainer_mode'] == 'eval':\n        eval_ckpts = []\n        eval_path = args.eval_path\n        assert eval_path != '', f'eval path is none'\n        \n        if os.path.isfile(eval_path):\n            eval_ckpts.append(eval_path)\n\n        elif os.path.isdir(eval_path):\n            ckpt_dirs = os.listdir(eval_path) \n            ckpt_dirs = [taylor for taylor in ckpt_dirs if os.path.isdir(os.path.join(eval_path, taylor))]\n            # epc[1]_iter[5000]_sap[60009]\n            ckpt_dirs = sorted([billie for billie in ckpt_dirs if billie.startswith('epc')], key=lambda x:int(x.split('sap[')[-1][:-1]))\n            eval_ckpts = [os.path.join(eval_path, cd, f'ckpt.pth.tar') for cd in ckpt_dirs]\n            eval_ckpts = [eval_c for eval_c in eval_ckpts if os.path.exists(eval_c)]\n        else:\n            raise ValueError()\n        configs['eval_ckpts'] = eval_ckpts\n    else:\n        pass\n     \n    gpu_ids = list(os.environ['CUDA_VISIBLE_DEVICES'].split(','))\n    assert len(set(gpu_ids)) == len(gpu_ids)\n    gpu_ids = list(range(len(gpu_ids)))\n    \n    if len(gpu_ids) > 1:\n        torch.multiprocessing.spawn(run, nprocs=len(gpu_ids), args=(configs, len(gpu_ids)))\n    elif len(gpu_ids) == 1:\n        run(rank=0, configs=configs, world_size=len(gpu_ids))"
  },
  {
    "path": "models/VIS/BackboneEncoderDecoder_WithScaleConsistency.py",
    "content": "import matplotlib.pyplot as plt\nfrom typing import Any, Optional, List, Dict, Set, Callable\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom data_schedule import build_schedule\nfrom torch import Tensor\nfrom einops import repeat, rearrange, reduce\nfrom functools import partial\nfrom einops.layers.torch import Rearrange\nfrom torch import einsum\nimport numpy as np\nimport logging\nfrom data_schedule.vis.apis import VIS_TrainAPI_clipped_video, VIS_Aug_CallbackAPI\nfrom data_schedule.vis.apis import VIS_EvalAPI_clipped_video_request_ann\nimport torchvision.transforms.functional as Trans_F\nimport copy\nfrom models.registry import register_model\nfrom models.optimization.optimizer import get_optimizer\nfrom models.optimization.scheduler import build_scheduler \nfrom models.backbone.utils import VideoMultiscale_Shape\nfrom detectron2.modeling import BACKBONE_REGISTRY, META_ARCH_REGISTRY\n\nclass BackboneEncoderDecoder_WithScaleConsistency(nn.Module):\n    def __init__(\n        self,\n        configs,\n        pixel_mean = [0.485, 0.456, 0.406],\n        pixel_std = [0.229, 0.224, 0.225],):\n        super().__init__()\n        self.register_buffer(\"pixel_mean\", torch.Tensor(pixel_mean).view(-1, 1, 1), False) # 3 1 1\n        self.register_buffer(\"pixel_std\", torch.Tensor(pixel_std).view(-1, 1, 1), False)\n        self.loss_weight = configs['model']['loss_weight']\n        video_backbone_configs = configs['model']['video_backbone'] \n        video_backbone_cls = BACKBONE_REGISTRY.get(video_backbone_configs['name'])\n        self.video_backbone = video_backbone_cls(video_backbone_configs)\n        self.max_stride = self.video_backbone.max_stride\n\n        self.fusion_encoder = META_ARCH_REGISTRY.get(configs['model']['fusion']['name'])(configs['model']['fusion'],\n                                                                                   multiscale_shapes=self.video_backbone.multiscale_shapes)\n          \n        same_dim_multiscale_shapes = VideoMultiscale_Shape.set_multiscale_same_dim(shape_by_dim=self.video_backbone.multiscale_shapes,\n                                                                                   same_dim=configs['model']['fusion']['d_model'])  \n         \n        self.decoder = META_ARCH_REGISTRY.get(configs['model']['decoder']['name'])(configs['model']['decoder'],\n                                                                                   multiscale_shapes=same_dim_multiscale_shapes)\n        if configs['model']['fusion']['name'] == 'Video_Deform2D_DividedTemporal_MultiscaleEncoder_v2':\n            self.fusion_encoder.hack_ref(query_norm=self.decoder.temporal_query_norm, mask_mlp=self.decoder.query_mask)\n        \n        self.test_clip_size = configs['model']['test_clip_size']\n    @property\n    def device(self):\n        return self.pixel_mean.device\n    \n    def model_preds(self, videos, video_aux_dict,):\n        if (not self.training) and (self.test_clip_size is not None):\n            nf = videos.shape[2]\n            clip_outputs = [] # list[dict]\n            for start_idx in range(0, nf, self.test_clip_size):\n                multiscales = self.video_backbone(x=videos[:, :, start_idx:(start_idx + self.test_clip_size)]) # b c t h w\n                multiscales = self.fusion_encoder(multiscales, video_aux_dict=video_aux_dict) \n                clip_outputs.append(self.decoder(multiscales, video_aux_dict=video_aux_dict)[-1])  # b t nq h w\n            return [{\n                'pred_masks': torch.cat([haosen['pred_masks'] for haosen in clip_outputs], dim=1), # b t n h w\n                'pred_class':  torch.cat([haosen['pred_class'] for haosen in clip_outputs], dim=1),\n            }]\n        # b 3 t h w -> b 3 t h w\n        multiscales = self.video_backbone(x=videos) # b c t h w\n        multiscales = self.fusion_encoder(multiscales, video_aux_dict=video_aux_dict) \n        return self.decoder(multiscales, video_aux_dict=video_aux_dict)\n\n    def forward(self, batch_dict):\n        assert self.training\n        VIS_TrainAPI_clipped_video\n        videos = batch_dict['video_dict']['videos'] \n        targets = batch_dict['targets']\n        batch_size, nf = videos.shape[:2]\n        videos = (videos - self.pixel_mean) / self.pixel_std\n        size1          = np.random.choice([256, 288, 320, 352, 384, 416, 448])\n        vid_1         = F.interpolate(videos.flatten(0, 1), size=size1, mode='bilinear')\n        vid_1          = rearrange(vid_1, '(b T) c h w -> b c T h w',b=batch_size, T=nf)\n        pred1          = self.model_preds(vid_1, video_aux_dict=batch_dict['video_dict']) # {pred_masks: b 1 t h w}\n        pred1_loss = self.decoder.compute_loss(pred1, targets=targets, frame_targets=batch_dict['frame_targets'],\n                                               video_aux_dict=batch_dict['video_dict'])\n        loss_value_dict = {key: pred1_loss[key] for key in list(self.loss_weight.keys())}\n        return loss_value_dict, self.loss_weight\n\n    @torch.no_grad()\n    def sample(self, batch_dict):\n        assert not self.training\n        VIS_EvalAPI_clipped_video_request_ann\n        videos = batch_dict['video_dict']['videos'] # b t 3 h w, 0-1\n        orig_t, _, orig_h, orig_w = batch_dict['video_dict']['orig_sizes'][0]\n        videos = (videos - self.pixel_mean) / self.pixel_std\n        assert videos.shape[0] == 1\n        batch_size, T, _, H, W = videos.shape\n        videos = videos.permute(0, 2, 1,3,4) # b c t h w\n        decoder_output = self.model_preds(videos, video_aux_dict=batch_dict['video_dict']) # {pred_masks: b 1 t h w}\n        if isinstance(decoder_output, list):\n            decoder_output = decoder_output[-1]\n        pred_masks = decoder_output['pred_masks'][0] # T n h w\n        pred_masks = F.interpolate(pred_masks, size=(H, W), mode='bilinear') > 0 # T n h w\n        pred_masks = pred_masks[:orig_t, :, :orig_h, :orig_w] # T n h w\n        #\n        pred_classes = decoder_output['pred_class'][0][:orig_t, :,:] # T n c, probability\n        pred_classes = pred_classes.cpu().unbind(0) # list[n c], T\n        pred_masks = pred_masks.cpu().unbind(0) # list[n h w], T\n\n        VIS_Aug_CallbackAPI\n        orig_video = videos[0][:, :orig_t, :orig_h, :orig_w].permute(1,0,2,3) # T 3 h w\n        orig_video = Trans_F.normalize(orig_video, [0, 0, 0], 1 / self.pixel_std)\n        orig_video = Trans_F.normalize(orig_video, -self.pixel_mean, [1, 1, 1]).cpu()\n\n        return {\n            'video': [orig_video], # [t 3 h w], 1\n            'pred_masks': [pred_masks], # [list[n h w], t, bool], 1\n            'pred_class': [pred_classes], # [list[n c], t, probability], 1\n        }\n\n    @staticmethod\n    def get_optim_params_group(model, configs):\n        weight_decay_norm = configs['optim']['weight_decay_norm']\n        weight_decay_embed = configs['optim']['weight_decay_embed']\n\n        defaults = {}\n        defaults['lr'] = configs['optim']['base_lr']\n        defaults['weight_decay'] = configs['optim']['weight_decay']\n\n        norm_module_types = (\n            torch.nn.BatchNorm1d,\n            torch.nn.BatchNorm2d,\n            torch.nn.BatchNorm3d,\n            torch.nn.SyncBatchNorm,\n            # NaiveSyncBatchNorm inherits from BatchNorm2d\n            torch.nn.GroupNorm,\n            torch.nn.InstanceNorm1d,\n            torch.nn.InstanceNorm2d,\n            torch.nn.InstanceNorm3d,\n            torch.nn.LayerNorm,\n            torch.nn.LocalResponseNorm,\n        )    \n        params: List[Dict[str, Any]] = []\n        memo: Set[torch.nn.parameter.Parameter] = set()\n        log_lr_group_idx = {'backbone':None, 'base':None}\n\n        for module_name, module in model.named_modules():\n            for module_param_name, value in module.named_parameters(recurse=False):\n                if not value.requires_grad:\n                    continue\n                # Avoid duplicating parameters\n                if value in memo:\n                    continue\n                memo.add(value)\n                hyperparams = copy.copy(defaults)\n                if \"video_backbone\" in module_name:\n                    hyperparams[\"lr\"] = hyperparams[\"lr\"] * configs['optim']['backbone_lr_multiplier']    \n                    if log_lr_group_idx['backbone'] is None:\n                        log_lr_group_idx['backbone'] = len(params)\n\n                else:\n                    if log_lr_group_idx['base'] is None:\n                        log_lr_group_idx['base'] = len(params)\n                                     \n                # pos_embed, norm, embedding的weight decay特殊对待\n                if (\n                    \"relative_position_bias_table\" in module_param_name\n                    or \"absolute_pos_embed\" in module_param_name\n                ):\n                    logging.debug(f'setting weight decay of {module_name}.{module_param_name} to zero')\n                    hyperparams[\"weight_decay\"] = 0.0\n                if isinstance(module, norm_module_types):\n                    hyperparams[\"weight_decay\"] = weight_decay_norm\n                if isinstance(module, torch.nn.Embedding):\n                    hyperparams[\"weight_decay\"] = weight_decay_embed\n                params.append({\"params\": [value], **hyperparams})\n   \n        return params, log_lr_group_idx\n\n@register_model\ndef backbone_encoder_decoder_withScaleConsistency(configs, device):\n    from .aux_mapper import AUXMapper_v1\n    model = BackboneEncoderDecoder_WithScaleConsistency(configs)\n    model.to(device)\n    params_group, log_lr_group_idx = BackboneEncoderDecoder_WithScaleConsistency.get_optim_params_group(model=model, configs=configs)\n    to_train_num_parameters = len([n for n, p in model.named_parameters() if p.requires_grad])\n    assert len(params_group) == to_train_num_parameters, f''\n    optimizer = get_optimizer(params_group, configs)\n\n    scheduler = build_scheduler(configs=configs, optimizer=optimizer)\n    model_input_mapper = AUXMapper_v1(configs['model']['input_aux'])\n\n    train_samplers, train_loaders, eval_function = build_schedule(configs, \n                                                                model_input_mapper.mapper, \n                                                                partial(model_input_mapper.collate, max_stride=model.max_stride))\n\n\n    return model, optimizer, scheduler,  train_samplers, train_loaders, log_lr_group_idx, eval_function\n\n\n"
  },
  {
    "path": "models/VIS/__init__.py",
    "content": "from . import BackboneEncoderDecoder_WithScaleConsistency\nfrom .. import modality_input_mappers\nfrom .. import backbone\nfrom .. import decoder\nfrom .. import encoder"
  },
  {
    "path": "models/VIS/aux_mapper.py",
    "content": "\nimport torch\n\nfrom torch.nn import functional as F\nfrom models.registry import register_model\nfrom data_schedule.utils.box_ops import box_xyxy_to_cxcywh\nfrom models.registry import MODELITY_INPUT_MAPPER_REGISTRY\nfrom data_schedule.vis.apis import VIS_TrainAPI_clipped_video\nfrom data_schedule.vis.apis import VIS_EvalAPI_clipped_video_request_ann\nfrom utils.misc import nested_tensor_from_videos_list_with_stride\n\nclass AUXMapper_v1:\n    def __init__(self, aux_configs):\n        video_auxes = aux_configs['video_auxes']\n\n        video_auxes_names = [config['name'] for config in video_auxes]\n        assert len(list(set(video_auxes_names))) == len(video_auxes_names), '每个aux的名字必须不一样'\n        self.video_auxes_names = video_auxes_names\n        self.video_auxes = [MODELITY_INPUT_MAPPER_REGISTRY.get(config['name'])(config) for config in video_auxes]\n\n        self.targets_auxes = None\n\n    def mapper(self, data_dict, mode,):\n        if mode == 'train':\n            VIS_TrainAPI_clipped_video\n            video = data_dict['video_dict']['video']\n            for aux, aux_name in zip(self.video_auxes, self.video_auxes_names):\n                data_dict['video_dict'][aux_name] = aux.mapper(video)\n        \n        elif mode == 'evaluate':\n            VIS_EvalAPI_clipped_video_request_ann\n            video = data_dict['video_dict']['video']\n            for aux, aux_name in zip(self.video_auxes, self.video_auxes_names):\n                data_dict['video_dict'][aux_name] = aux.mapper(video) \n        else:\n            raise ValueError()\n           \n        return data_dict\n\n    def collate(self, batch_dict, mode, max_stride):\n        if mode == 'train':\n            VIS_TrainAPI_clipped_video\n            video_dict = self.collate_video_dict(batch_dict, max_stride=max_stride)\n            targets = [sample['targets'] for sample in batch_dict]\n            frame_has_ann = [clip_tgt['has_ann'] for clip_tgt in targets] # list[t], b\n            frame_targets = [sample['frame_targets'] for sample in batch_dict]\n\n            _, pad_T, _, pad_H, pad_W = video_dict['videos'].shape\n            targets = self.collate_targets(targets=targets, pad_H=pad_H, pad_W=pad_W, pad_T=pad_T)\n            frame_targets = self.collate_frame_targets(frame_targets=frame_targets, \n                                                                frame_has_ann=frame_has_ann,\n                                                                pad_H=pad_H, pad_W=pad_W, pad_T=pad_T)\n            \n            ret = {\n                'video_dict': video_dict,\n                'targets': targets,\n                'frame_targets': frame_targets,\n                'meta_idxs':  [sample['meta_idx'] for sample in batch_dict],\n                'visualize': [sample['visualize'] for sample in batch_dict],               \n            }   \n                        \n        elif mode == 'evaluate':\n            VIS_EvalAPI_clipped_video_request_ann\n            assert len(batch_dict) == 1\n            video_dict = self.collate_video_dict(batch_dict, max_stride=max_stride) # 不pad\n            metas = [sample['meta'] for sample in batch_dict]\n\n            collated_metas = {}\n            for key in metas[0].keys():\n                collated_metas[key] = [mt[key] for mt in metas]\n            ret = {\n                'video_dict': video_dict,\n                'metas': collated_metas,\n                'meta_idxs':  [sample['meta_idx'] for sample in batch_dict],\n                'visualize': [sample['visualize'] for sample in batch_dict],  \n            }  \n        debug_data = False\n        if debug_data:\n            self.visualize_input_target_for_debug_data(ret) # ./test.png\n        return ret\n\n    def collate_video_dict(self, batch_dict, max_stride):\n        videos = [sample['video_dict']['video'] for sample in batch_dict]  # list[ti 3 hi wi] -> b T 3 H W\n        orig_sizes = [list(vid.shape) for vid in videos] # t 3 h w\n        if type(max_stride) == int: # temporal max stride 为1, spatial max stride\n            pad_stride = [1, max_stride]\n        if (type(max_stride) == list) and (len(max_stride) == 2):\n            pad_stride = max_stride\n        videos = nested_tensor_from_videos_list_with_stride(videos, max_stride=pad_stride).tensors # b t c h w\n        video_dicts = {'videos': videos, 'orig_sizes': orig_sizes}\n\n        for aux_name, aux in zip(self.video_auxes_names, self.video_auxes):\n            auxes = [sample['video_dict'][aux_name] for sample in batch_dict] # list[dict] / list[tensor]\n            collated_auxes = aux.collate(auxes, batch_videos=videos) # list[dict] / tensor\n            if isinstance(auxes[0], dict):\n                keys = collated_auxes.keys()\n                for key in keys:\n                    assert key not in video_dicts\n                    video_dicts[key] = collated_auxes[key]\n            else:\n                video_dicts[aux_name] = collated_auxes\n\n        return video_dicts\n\n    def collate_frame_targets(self, frame_targets, frame_has_ann, pad_H, pad_W, pad_T): # \n        VIS_TrainAPI_clipped_video\n        ret = {}\n        has_ann = torch.stack([F.pad(ha.float(), pad=(0, pad_T - len(ha)), value=0.).bool() for ha in frame_has_ann], dim=0).flatten() # bT\n        ret['has_ann'] = has_ann\n        masks = [ftarget['masks'] for sample in frame_targets for ftarget in sample] # list[ni h w], bt'\n        masks = [F.pad(m.float(), pad=(0, pad_W-m.shape[-1], 0, pad_H-m.shape[-2])).bool() for m in masks] # list[ni H W], bt'\n        ret['masks'] = masks # list[ni h w], bt'\n\n        classes = [ftarget['classes'] for sample in frame_targets for ftarget in sample] # list[ni], bt'\n        ret['classes'] = classes\n        \n        if 'boxes' in frame_targets[0][0]:\n            boxes = [ftarget['boxes'] for sample in frame_targets for ftarget in sample] # list[ni 4], x1y1x2y2, bt'\n            boxes = [box_xyxy_to_cxcywh(bx) for bx in boxes]\n            boxes = [bx / torch.tensor([pad_W, pad_H, pad_W, pad_H], dtype=bx.dtype) for bx in boxes] # 0-1\n            ret['boxes'] = boxes # list[ni 4], bt' \n        return ret\n\n    def collate_targets(self, targets, pad_H, pad_W, pad_T):\n        VIS_TrainAPI_clipped_video\n        has_ann = [sample['has_ann'] for sample in targets] # list[t], bool\n        has_ann = torch.stack([F.pad(ha.float(), pad=(0, pad_T - len(ha)), value=0.).bool() for ha in has_ann], dim=0) # b T\n        masks = [sample['masks'] for sample in targets] \n        masks = [F.pad(m.float(), pad=(0, pad_W-m.shape[-1], 0, pad_H-m.shape[-2]), value=0.).bool() \\\n                 for m in masks] # list[ni T' H W]\n        classes = [sample['classes'] for sample in targets]\n        ret = {\n            'masks': masks, # list[ni T' h w]\n            'has_ann': has_ann, # b T\n            'classes': classes, # list[ni], b\n        } \n        if 'boxes' in targets[0]:\n            boxes = [sample['boxes'] for sample in targets] # list[ni T' 4], x1y1x2y2\n            boxes = [box_xyxy_to_cxcywh(bx) for bx in boxes]\n            boxes = [bx / torch.tensor([pad_W, pad_H, pad_W, pad_H], dtype=torch.float) for bx in boxes] # 0-1\n            ret.update({'boxes': boxes,})\n        return ret\n\n    def visualize_input_target_for_debug_data(self, ret):\n        videos = ret['video_dict']['videos'] # b T 3 H W\n        pass\n"
  },
  {
    "path": "models/__init__.py",
    "content": "import os\nfrom .registry import model_entrypoint\n\nif os.getenv('CURRENT_TASK') == 'VIS':\n    from . import VIS\nelse:\n    raise ValueError()\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "models/backbone/__init__.py",
    "content": "\nfrom . import res2net, pvtv2"
  },
  {
    "path": "models/backbone/pvtv2.py",
    "content": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom timm.models.layers import DropPath\nfrom timm.models.registry import register_model\n\nclass DWConv(nn.Module):\n    def __init__(self, dim):\n        super(DWConv, self).__init__()\n        self.dwconv = nn.Conv2d(dim, dim, 3, 1, 1, groups=dim)\n\n    def forward(self, x, H, W):\n        B,N,C = x.shape\n        x     = x.transpose(1, 2).view(B, C, H, W)\n        x     = self.dwconv(x)\n        x     = x.flatten(2).transpose(1, 2)\n        return x\n\nclass Mlp(nn.Module):\n    def __init__(self, in_features, hidden_features):\n        super().__init__()\n        self.fc1        = nn.Linear(in_features, hidden_features)\n        self.dwconv     = DWConv(hidden_features)\n        self.fc2        = nn.Linear(hidden_features, in_features)\n\n    def forward(self, x, H, W):\n        x = self.fc1(x)\n        x = F.gelu(self.dwconv(x, H, W))\n        x = self.fc2(x)\n        return x\n\nclass Attention(nn.Module):\n    def __init__(self, dim, num_heads, sr_ratio):\n        super().__init__()\n        assert dim % num_heads == 0, f\"dim {dim} should be divided by num_heads {num_heads}.\"\n\n        self.num_heads = num_heads\n        self.scale     = (dim//num_heads)**(-0.5)\n        self.q         = nn.Linear(dim, dim)\n        self.kv        = nn.Linear(dim, dim*2)\n        self.proj      = nn.Linear(dim, dim)\n        self.sr_ratio  = sr_ratio\n        if sr_ratio > 1:\n            self.sr    = nn.Conv2d(dim, dim, kernel_size=sr_ratio, stride=sr_ratio)\n            self.norm  = nn.LayerNorm(dim)\n\n    def forward(self, x, H, W):\n        B, N, C = x.shape\n        q       = self.q(x).reshape(B, N, self.num_heads, C//self.num_heads).permute(0, 2, 1, 3)\n\n        if self.sr_ratio > 1:\n            x_ = x.permute(0, 2, 1).reshape(B, C, H, W)\n            x_ = self.sr(x_).reshape(B, C, -1).permute(0, 2, 1)\n            x_ = self.norm(x_)\n            kv = self.kv(x_).reshape(B, -1, 2, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)\n        else:\n            kv = self.kv(x).reshape(B, -1, 2, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)\n \n        k, v = kv[0], kv[1]\n        attn = (q @ k.transpose(-2, -1)) * self.scale\n        attn = attn.softmax(dim=-1)\n        x    = (attn @ v).transpose(1, 2).reshape(B, N, C)\n        x    = self.proj(x)\n        return x\n\nclass Block(nn.Module):\n    def __init__(self, dim, num_heads, mlp_ratio, drop_path, sr_ratio):\n        super().__init__()\n        self.norm1     = nn.LayerNorm(dim, eps=1e-6)\n        self.attn      = Attention(dim, num_heads=num_heads, sr_ratio=sr_ratio)\n        self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()\n        self.norm2     = nn.LayerNorm(dim, eps=1e-6)\n        self.mlp       = Mlp(in_features=dim, hidden_features=int(dim*mlp_ratio))\n\n    def forward(self, x, H, W):\n        x = x + self.drop_path(self.attn(self.norm1(x), H, W))\n        x = x + self.drop_path(self.mlp(self.norm2(x), H, W))\n        return x\n\nclass OverlapPatchEmbed(nn.Module):\n    def __init__(self, patch_size, stride, in_chans, embed_dim):\n        super().__init__()\n        self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=stride, padding=(patch_size//2, patch_size//2))\n        self.norm = nn.LayerNorm(embed_dim)\n\n    def forward(self, x):\n        x       = self.proj(x)\n        B,C,H,W = x.shape\n        x       = x.flatten(2).transpose(1, 2)\n        x       = self.norm(x)\n        return x, H, W\n\nclass PVT(nn.Module):\n    def __init__(self, embed_dims, mlp_ratios, depths, snapshot, sr_ratios=[8, 4, 2, 1]):\n        super().__init__()\n        self.depths       = depths\n        self.snapshot     = snapshot\n        # patch_embed\n        self.patch_embed1 = OverlapPatchEmbed(patch_size=7, stride=4, in_chans=3,             embed_dim=embed_dims[0])\n        self.patch_embed2 = OverlapPatchEmbed(patch_size=3, stride=2, in_chans=embed_dims[0], embed_dim=embed_dims[1])\n        self.patch_embed3 = OverlapPatchEmbed(patch_size=3, stride=2, in_chans=embed_dims[1], embed_dim=embed_dims[2])\n        self.patch_embed4 = OverlapPatchEmbed(patch_size=3, stride=2, in_chans=embed_dims[2], embed_dim=embed_dims[3])\n        # transformer encoder\n        dpr = [x.item() for x in torch.linspace(0, 0.1, sum(depths))]  # stochastic depth decay rule\n        cur = 0\n        self.block1 = nn.ModuleList([Block(dim=embed_dims[0], num_heads=1, mlp_ratio=mlp_ratios[0], drop_path=dpr[cur + i], sr_ratio=sr_ratios[0]) for i in range(depths[0])])\n        self.norm1  = nn.LayerNorm(embed_dims[0], eps=1e-6)\n\n        cur += depths[0]\n        self.block2 = nn.ModuleList([Block(dim=embed_dims[1], num_heads=2, mlp_ratio=mlp_ratios[1], drop_path=dpr[cur + i], sr_ratio=sr_ratios[1]) for i in range(depths[1])])\n        self.norm2  = nn.LayerNorm(embed_dims[1], eps=1e-6)\n\n        cur += depths[1]\n        self.block3 = nn.ModuleList([Block(dim=embed_dims[2], num_heads=5, mlp_ratio=mlp_ratios[2], drop_path=dpr[cur + i], sr_ratio=sr_ratios[2]) for i in range(depths[2])])\n        self.norm3  = nn.LayerNorm(embed_dims[2], eps=1e-6)\n\n        cur += depths[2]\n        self.block4 = nn.ModuleList([Block(dim=embed_dims[3], num_heads=8, mlp_ratio=mlp_ratios[3], drop_path=dpr[cur + i], sr_ratio=sr_ratios[3]) for i in range(depths[3])])\n        self.norm4  = nn.LayerNorm(embed_dims[3], eps=1e-6)\n\n        state_dict:dict = torch.load(self.snapshot, map_location='cpu')\n        state_dict.pop(\"head.weight\")\n        state_dict.pop(\"head.bias\")\n        self.load_state_dict(state_dict, strict=True)\n        del state_dict\n\n    @torch.jit.ignore\n    def no_weight_decay(self):\n        return {'pos_embed1', 'pos_embed2', 'pos_embed3', 'pos_embed4', 'cls_token'}  # has pos_embed may be better\n\n    def forward(self, x):\n        B = x.shape[0]\n        # stage 1\n        out1, H, W = self.patch_embed1(x)\n        for i, blk in enumerate(self.block1):\n            out1 = blk(out1, H, W)\n        out1 = self.norm1(out1).reshape(B, H, W, -1).permute(0, 3, 1, 2).contiguous()\n\n        # stage 2\n        out2, H, W = self.patch_embed2(out1)\n        for i, blk in enumerate(self.block2):\n            out2 = blk(out2, H, W)\n        out2 = self.norm2(out2).reshape(B, H, W, -1).permute(0, 3, 1, 2).contiguous()\n\n        # stage 3\n        out3, H, W = self.patch_embed3(out2)\n        for i, blk in enumerate(self.block3):\n            out3 = blk(out3, H, W)\n        out3 = self.norm3(out3).reshape(B, H, W, -1).permute(0, 3, 1, 2).contiguous()\n\n        # stage 4\n        out4, H, W = self.patch_embed4(out3)\n        for i, blk in enumerate(self.block4):\n            out4 = blk(out4, H, W)\n        out4 = self.norm4(out4).reshape(B, H, W, -1).permute(0, 3, 1, 2).contiguous()\n        return out1, out2, out3, out4\n\n\nfrom detectron2.modeling import BACKBONE_REGISTRY\nfrom einops import rearrange, reduce, repeat\nfrom .utils import VideoMultiscale_Shape, ImageMultiscale_Shape\nimport os\nimport time\n\n\n@BACKBONE_REGISTRY.register()\nclass PVT_V2(nn.Module):\n    def __init__(self, configs) -> None:\n        super().__init__()\n        pt_path = os.getenv('PT_PATH')\n        pvt_v2 = PVT(embed_dims=[64, 128, 320, 512], mlp_ratios=[8, 8, 4, 4], \n                     depths=[3, 4, 6, 3], snapshot=os.path.join(pt_path, 'pvt_v2/pvt_v2_b2.pth'))\n        self.pvt_v2 = pvt_v2\n\n        freeze = configs['freeze']\n\n        if freeze:\n            for p in self.parameters():\n                p.requires_grad_(False)\n\n        self.multiscale_shapes = {}\n        for name, spatial_stride, dim  in zip(['res2', 'res3', 'res4', 'res5'], \n                                              [4, 8, 16, 32],\n                                              [64, 128, 320, 512]):\n            self.multiscale_shapes[name] =  ImageMultiscale_Shape(spatial_stride=spatial_stride, dim=dim)\n        self.max_stride = 32\n\n    def forward(self, x):\n\n        if not self.training:\n            batch_feats = []\n            for haosen in x:\n                feats =  self.pvt_v2(haosen.unsqueeze(0))\n                batch_feats.append(feats)\n            batch_feats = list(zip(*batch_feats)) # 4\n            batch_feats = [torch.cat(haosen, dim=0) for haosen in batch_feats] # list[bt c h w]\n            ret = {}\n            names = ['res2', 'res3', 'res4', 'res5']\n            for name, feat in zip(names, batch_feats):\n                ret[name] = feat\n            return ret            \n        else:\n            layer_outputs = self.pvt_v2(x)\n            ret = {}\n            names = ['res2', 'res3', 'res4', 'res5']\n            for name, feat in zip(names, layer_outputs):\n                ret[name] = feat\n            return ret\n\n\n    def num_parameters(self):\n        return sum(p.numel() for p in self.parameters() if p.requires_grad)\n\n@BACKBONE_REGISTRY.register()\nclass Video2D_PVT_V2(nn.Module):\n    def __init__(self, configs) -> None:\n        super().__init__()\n        self.image_homo = PVT_V2(configs=configs)\n\n        self.multiscale_shapes = {}\n        for name, temporal_stride, spatial_stride, dim  in zip(['res2', 'res3', 'res4', 'res5'],  \n                                                               [1, 1, 1, 1], \n                                                               [4, 8, 16, 32],\n                                                               [64, 128, 320, 512]):\n            self.multiscale_shapes[name] =  VideoMultiscale_Shape(temporal_stride=temporal_stride, \n                                                                  spatial_stride=spatial_stride, dim=dim)\n        self.max_stride = [1, 32]\n\n    \n    def forward(self, x):\n        batch_size, _, T = x.shape[:3]\n        x = rearrange(x, 'b c t h w -> (b t) c h w').contiguous()\n        layer_outputs = self.image_homo(x)\n\n        layer_outputs = {key: rearrange(value.contiguous(), '(b t) c h w -> b c t h w',b=batch_size, t=T).contiguous() \\\n                         for key, value in layer_outputs.items()}\n        return layer_outputs\n    \n    def num_parameters(self):\n        return sum(p.numel() for p in self.parameters() if p.requires_grad)"
  },
  {
    "path": "models/backbone/res2net.py",
    "content": "import math\nimport torch\nimport torch.nn as nn\nimport os\nimport torch.nn.functional as F\nfrom detectron2.modeling import BACKBONE_REGISTRY\nfrom einops import rearrange, reduce, repeat\nfrom .utils import VideoMultiscale_Shape\n\nclass Bottle2neck(nn.Module):\n    expansion = 4\n    def __init__(self, inplanes, planes, stride=1, downsample=None, baseWidth=26, scale=4, stype='normal'):\n        super(Bottle2neck, self).__init__()\n        width      = int(math.floor(planes*(baseWidth/64.0)))\n        self.conv1 = nn.Conv2d(inplanes, width*scale, kernel_size=1, bias=False)\n        self.bn1   = nn.BatchNorm2d(width*scale)\n        self.nums  = 1 if scale == 1 else scale - 1\n        if stype == 'stage':\n            self.pool = nn.AvgPool2d(kernel_size=3, stride=stride, padding=1)\n        convs, bns = [], []\n        for i in range(self.nums):\n            convs.append(nn.Conv2d(width, width, kernel_size=3, stride=stride, padding=1, bias=False))\n            bns.append(nn.BatchNorm2d(width))\n        self.convs = nn.ModuleList(convs)\n        self.bns   = nn.ModuleList(bns)\n        self.conv3 = nn.Conv2d(width * scale, planes * self.expansion, kernel_size=1, bias=False)\n        self.bn3   = nn.BatchNorm2d(planes * self.expansion)\n        self.downsample = downsample\n        self.stype = stype\n        self.scale = scale\n        self.width = width\n\n    def forward(self, x):\n        out = F.relu(self.bn1(self.conv1(x)), inplace=True)\n        spx = torch.split(out, self.width, 1)\n        for i in range(self.nums):\n            sp  = spx[i] if i == 0 or self.stype == 'stage' else sp + spx[i]\n            sp  = self.convs[i](sp)\n            sp  = F.relu(self.bns[i](sp), inplace=True)\n            out = sp if i == 0 else torch.cat((out, sp), 1)\n        if self.scale != 1 and self.stype == 'normal':\n            out = torch.cat((out, spx[self.nums]), 1)\n        elif self.scale != 1 and self.stype == 'stage':\n            out = torch.cat((out, self.pool(spx[self.nums])), 1)\n\n        out = self.bn3(self.conv3(out))\n        if self.downsample is not None:\n            x = self.downsample(x)\n        return F.relu(out+x, inplace=True)\n\nclass Res2Net(nn.Module):\n    def __init__(self, layers, snapshot, baseWidth=26, scale=4):\n        super(Res2Net, self).__init__()\n        self.inplanes  = 64\n        self.snapshot  = snapshot\n        self.baseWidth = baseWidth\n        self.scale     = scale\n        self.conv1     = nn.Sequential(\n                                nn.Conv2d(3, 32, 3, 2, 1, bias=False),\n                                nn.BatchNorm2d(32),\n                                nn.ReLU(inplace=True),\n                                nn.Conv2d(32, 32, 3, 1, 1, bias=False),\n                                nn.BatchNorm2d(32),\n                                nn.ReLU(inplace=True),\n                                nn.Conv2d(32, 64, 3, 1, 1, bias=False)\n                            )\n        self.bn1    = nn.BatchNorm2d(64)\n        self.layer1 = self._make_layer(Bottle2neck, 64, layers[0])\n        self.layer2 = self._make_layer(Bottle2neck, 128, layers[1], stride=2)\n        self.layer3 = self._make_layer(Bottle2neck, 256, layers[2], stride=2)\n        self.layer4 = self._make_layer(Bottle2neck, 512, layers[3], stride=2)\n\n        state_dict:dict = torch.load(self.snapshot, map_location='cpu')\n        self.load_state_dict(state_dict, strict=False)\n        del state_dict\n\n    def _make_layer(self, block, planes, blocks, stride=1):\n        downsample = None\n        if stride != 1 or self.inplanes != planes * block.expansion:\n            downsample = nn.Sequential(\n                nn.AvgPool2d(kernel_size=stride, stride=stride, ceil_mode=True, count_include_pad=False),\n                nn.Conv2d(self.inplanes, planes * block.expansion, kernel_size=1, stride=1, bias=False),\n                nn.BatchNorm2d(planes * block.expansion),\n            )\n\n        layers        = [block(self.inplanes, planes, stride, downsample=downsample, stype='stage', baseWidth=self.baseWidth, scale=self.scale)]\n        self.inplanes = planes * block.expansion\n        for i in range(1, blocks):\n            layers.append(block(self.inplanes, planes, baseWidth=self.baseWidth, scale=self.scale))\n        return nn.Sequential(*layers)\n\n    def forward(self, x):\n        out1 = F.relu(self.bn1(self.conv1(x)), inplace=True)\n        out1 = F.max_pool2d(out1, kernel_size=3, stride=2, padding=1)\n        out2 = self.layer1(out1)\n        out3 = self.layer2(out2)\n        out4 = self.layer3(out3)\n        out5 = self.layer4(out4)\n        return out2, out3, out4, out5\n\n    def initialize(self):\n        self.load_state_dict(torch.load(self.snapshot), strict=False)\n\n@BACKBONE_REGISTRY.register()\nclass Res2Net_50_EachFrame(nn.Module):\n    def __init__(self, configs) -> None:\n        super().__init__()\n        pt_path = os.getenv('PT_PATH')\n        res2net = Res2Net([3, 4, 6, 3], os.path.join(pt_path, 'res2net/res2net50_v1b_26w_4s-3cf99910.pth'))\n        self.res2net = res2net\n\n        freeze = configs['freeze']\n\n        if freeze:\n            for p in self.parameters():\n                p.requires_grad_(False)\n\n        self.multiscale_shapes = {}\n        for name, temporal_stride, spatial_stride, dim  in zip(['res2', 'res3', 'res4', 'res5'],  \n                                                               [1, 1, 1, 1], \n                                                               [4, 8, 16, 32],\n                                                               [256, 512, 1024, 2048]):\n            self.multiscale_shapes[name] =  VideoMultiscale_Shape(temporal_stride=temporal_stride, \n                                                                  spatial_stride=spatial_stride, dim=dim)\n\n        self.max_stride = [1, 32]\n\n    \n    def forward(self, x):\n        batch_size, _, T = x.shape[:3]\n        x = rearrange(x, 'b c t h w -> (b t) c h w')\n        layer_outputs = self.res2net(x)\n\n        ret = {}\n        names = ['res2', 'res3', 'res4', 'res5']\n        for name, feat in zip(names, layer_outputs):\n            ret[name] = rearrange(feat.contiguous(), '(b t) c h w -> b c t h w',b=batch_size, t=T)\n\n        return ret\n    \n\n    def num_parameters(self):\n        return sum(p.numel() for p in self.parameters() if p.requires_grad)       \n "
  },
  {
    "path": "models/backbone/utils.py",
    "content": "\nclass VideoMultiscale_Shape:\n    def __init__(self, temporal_stride, spatial_stride, dim) -> None:\n        self.temporal_stride = temporal_stride\n        self.spatial_stride = spatial_stride\n        self.dim = dim\n    \n    @staticmethod\n    def set_multiscale_same_dim(shape_by_dim, same_dim):\n        return {\n            key: VideoMultiscale_Shape(temporal_stride=value.temporal_stride,\n                                       spatial_stride=value.spatial_stride,\n                                       dim=same_dim) for key,value in shape_by_dim.items()\n        }\n\nclass ImageMultiscale_Shape:\n    def __init__(self, spatial_stride, dim) -> None:\n        self.spatial_stride = spatial_stride\n        self.dim = dim\n    \n\n\n"
  },
  {
    "path": "models/decoder/__init__.py",
    "content": "\nfrom . import mask2former_video"
  },
  {
    "path": "models/decoder/mask2former_video.py",
    "content": "\n# multi-scale features, b c h w -> module -> obj queries, predictions, b nq c\nimport torch.nn as nn\nfrom models.layers.decoder_layers import CrossAttentionLayer, SelfAttentionLayer, FFNLayer\nfrom models.layers.anyc_trans import MLP\nimport torch.nn.functional as F\nimport torch\nimport copy\nfrom models.layers.utils import zero_module, _get_clones\nfrom models.layers.position_encoding import build_position_encoding\nfrom einops import rearrange, reduce, repeat\nfrom scipy.optimize import linear_sum_assignment\nfrom models.layers.matching import batch_dice_loss, batch_sigmoid_ce_loss, batch_sigmoid_focal_loss, dice_loss, ce_mask_loss\nfrom detectron2.modeling import META_ARCH_REGISTRY\nimport detectron2.utils.comm as comm\nimport data_schedule.utils.box_ops as box_ops\nfrom models.layers.utils import zero_module\nfrom utils.misc import is_dist_avail_and_initialized\nfrom collections import defaultdict\nfrom detectron2.projects.point_rend.point_features import point_sample\nfrom torch.cuda.amp import autocast\nfrom detectron2.projects.point_rend.point_features import (\n    get_uncertain_point_coords_with_randomness,\n    point_sample,\n)\ndef calculate_uncertainty(logits):\n    \"\"\"\n    We estimate uncerainty as L1 distance between 0.0 and the logit prediction in 'logits' for the\n        foreground class in `classes`.\n    Args:\n        logits (Tensor): A tensor of shape (R, 1, ...) for class-specific or\n            class-agnostic, where R is the total number of predicted masks in all images and C is\n            the number of foreground classes. The values are logits.\n    Returns:\n        scores (Tensor): A tensor of shape (R, 1, ...) that contains uncertainty scores with\n            the most uncertain locations having the highest uncertainty score.\n    \"\"\"\n    assert logits.shape[1] == 1\n    gt_class_logits = logits.clone()\n    return -(torch.abs(gt_class_logits))\n\n\nclass Video_SetMatchingLoss(nn.Module):\n    def __init__(self, \n                 loss_config,\n                 num_classes,) -> None:\n        super().__init__()\n        self.num_classes = num_classes # n=1 / n=0 / n>1\n        self.matching_metrics = loss_config['matching_metrics'] # mask: mask/dice; point_sample_mask: ..\n        self.losses = loss_config['losses'] \n\n        self.aux_layer_weights = loss_config['aux_layer_weights']  # int/list\n        empty_weight = torch.ones(self.num_classes + 1)\n        empty_weight[-1] = loss_config['background_cls_eos']\n        self.register_buffer('empty_weight', empty_weight)\n        # self.register_buffer('small_obj_weight', torch.tensor(loss_config['small_obj_weight']).float())\n        self._warmup_iters = 2000\n        self.register_buffer(\"_iter\", torch.zeros([1]))\n\n    @property\n    def device(self,):\n        return self.empty_weight.device\n    \n    def compute_loss(self, \n                     model_outs, \n                     targets,\n                     video_aux_dict,\n                     **kwargs):\n        # list[n t' h w], batch\n        if 'masks' in targets:\n            num_objs = sum([haosen.flatten(1).any(-1).int().sum().item() for haosen in targets['masks']])\n        # list[n t' 4], batch\n        elif 'boxes' in targets:\n            # n t' 2 -> n t -> n\n            num_objs = sum([(haosen[:, :, 2:] > 0).all(-1).any(-1).int().sum().item() for haosen in targets['boxes']])\n        else:\n            raise ValueError('targets里没有boxes/masks, 需要确定数量')\n        \n        num_objs = torch.as_tensor([num_objs], dtype=torch.float, device=self.device)\n        if is_dist_avail_and_initialized():\n            torch.distributed.all_reduce(num_objs)\n        num_objs = torch.clamp(num_objs / comm.get_world_size(), min=1).item()\n        if isinstance(self.aux_layer_weights, list):\n            assert len(self.aux_layer_weights) == (len(model_outs) - 1)\n        else:\n            self.aux_layer_weights = [self.aux_layer_weights] * (len(model_outs) - 1)\n\n        layer_weights = self.aux_layer_weights + [1.]\n\n        loss_values = {\n            'mask_dice':0., 'mask_ce':0.,\n            'box_l1': 0., 'box_giou': 0.,\n            'class_ce':0.,\n\n            'mask_dice_smobj':0., 'mask_ce_smobj':0.,\n            'boxMask_dice':0., 'boxMask_ce':0.,\n        }\n        \n        if ('mask_ce_dice' in self.matching_metrics) or ('mask_ce_dice' in self.losses):\n            # mask interpolate\n            tgt_mask_shape = targets['masks'][0].shape[-2:] # list[n t H W], b\n            for layer_idx in range(len(model_outs)):\n                # b t nq h w\n                batch_size, nf = model_outs[layer_idx]['pred_masks'].shape[:2]\n                model_outs[layer_idx]['pred_masks'] = rearrange(F.interpolate(model_outs[layer_idx]['pred_masks'].flatten(0, 1),\n                                                                size=tgt_mask_shape, mode='bilinear', align_corners=False),\n                                                                    '(b t) n h w -> b t n h w',b=batch_size, t=nf)\n        for taylor, layer_out in zip(layer_weights, model_outs):\n            if taylor != 0:\n                matching_indices = self.matching(layer_out, targets)\n                for loss in self.losses:\n                    loss_extra_param = self.losses[loss]\n                    if loss == 'mask_dice_ce' :\n                        loss_dict = self.loss_mask_dice_ce(layer_out, targets, matching_indices, num_objs,\n                                                           loss_extra_param=loss_extra_param)\n                    elif loss == 'class_ce':\n                        loss_dict = self.loss_class_ce(layer_out, targets, matching_indices, num_objs,\n                                                       loss_extra_param=loss_extra_param)\n                    elif loss == 'point_mask_dice_ce':\n                        loss_dict = self.loss_point_mask_dice_ce(layer_out, targets, matching_indices, num_objs,\n                                                                 loss_extra_param=loss_extra_param)\n                    else:\n                        raise ValueError()\n                    \n                    for key, value in loss_dict.items():\n                        loss_values[key] = loss_values[key] + value\n    \n        return loss_values      \n\n    @torch.no_grad()\n    def matching(self, layer_out, targets):\n        batch_size = len(targets['masks']) if 'masks' in targets else len(targets['boxes'])\n        indices = [] \n        has_ann = targets['has_ann']\n        for i in range(batch_size):\n            C = 0.\n\n            if 'class_prob' in self.matching_metrics:\n                out_cls = layer_out['pred_class'][i].softmax(-1) # nq c\n                tgt_cls = targets['classes'][i] # n\n                cost_class = - out_cls[:, tgt_cls] # nq n\n                C += self.matching_metrics['class_prob']['prob'] * cost_class\n\n            if 'mask_dice_ce' in self.matching_metrics:\n                out_mask = layer_out['pred_masks'][i][has_ann[i]].permute(1, 0, 2, 3).contiguous()  # nq t' h w\n                tgt_mask = targets['masks'][i].to(out_mask) # ni t' H W\n                cost_mask = batch_sigmoid_ce_loss(out_mask.flatten(1), tgt_mask.flatten(1)) \n                cost_dice = batch_dice_loss(out_mask.flatten(1), tgt_mask.flatten(1))\n                C += self.matching_metrics['mask_dice_ce']['ce'] * cost_mask + \\\n                     self.matching_metrics['mask_dice_ce']['dice'] * cost_dice\n\n \n            if 'point_mask_dice_ce' in self.matching_metrics:\n                out_mask = layer_out['pred_masks'][i][has_ann[i]].permute(1, 0, 2, 3).contiguous() # nq t' h w\n                tgt_mask = targets['masks'][i].to(out_mask)# ni t' H W\n                nf = out_mask.shape[1]\n\n                out_mask = out_mask.flatten(0, 1)[:, None]\n                tgt_mask = tgt_mask.flatten(0, 1)[:, None]\n                # all masks share the same set of points for efficient matching!\n                point_coords = torch.rand(1, self.matching_metrics['point_mask_dice_ce']['num_points'],\n                                           2, device=self.device)\n                # get gt labels\n                tgt_mask = point_sample(\n                    tgt_mask,\n                    point_coords.repeat(tgt_mask.shape[0], 1, 1),\n                    align_corners=False,\n                ).squeeze(1) # nqt s\n                tgt_mask = rearrange(tgt_mask, '(nq t) s -> nq t s',t=nf)\n\n                out_mask = point_sample(\n                    out_mask,\n                    point_coords.repeat(out_mask.shape[0], 1, 1),\n                    align_corners=False,\n                ).squeeze(1) # nit s\n                out_mask = rearrange(out_mask, '(nq t) s -> nq t s',t=nf)\n                with autocast(enabled=False):\n                    out_mask = out_mask.float().flatten(1) # nq num_points\n                    tgt_mask = tgt_mask.float().flatten(1)\n                    cost_mask = batch_sigmoid_ce_loss(out_mask, tgt_mask)\n                    cost_dice = batch_dice_loss(out_mask, tgt_mask)\n                C += self.matching_metrics['point_mask_dice_ce']['ce'] * cost_mask + \\\n                     self.matching_metrics['point_mask_dice_ce']['dice'] * cost_dice\n            \n            C = C.cpu()\n            indices.append(linear_sum_assignment(C))\n\n        return [\n            (torch.as_tensor(i, dtype=torch.int64), torch.as_tensor(j, dtype=torch.int64))\n            for i, j in indices\n        ]\n\n    def loss_mask_dice_ce(self, outputs, targets, indices, num_objs, loss_extra_param):\n        has_ann = targets['has_ann'] # b t\n        src_masks = outputs['pred_masks'].permute(0, 2, 1, 3, 4).contiguous() # b nq t h w\n        tgt_masks = targets['masks'] # list[n t' h w]\n        # list[nq t' h w] -> n_sigma t' h w\n        src_masks = torch.cat([t[J][:, haosen] for t, (J, _), haosen in zip(src_masks, indices, has_ann)],dim=0) \n        tgt_masks = torch.cat([t[J] for t, (_, J) in zip(tgt_masks, indices)], dim=0)\n        tgt_masks = tgt_masks.to(src_masks)\n        losses = {\n            \"mask_ce\": ce_mask_loss(src_masks.flatten(0, 1).flatten(1), tgt_masks.flatten(0, 1).flatten(1), num_boxes=num_objs),\n            \"mask_dice\": dice_loss(src_masks.flatten(0, 1).flatten(1), tgt_masks.flatten(0, 1).flatten(1), num_boxes=num_objs),\n        }\n        return losses   \n\n    def loss_point_mask_dice_ce(self, outputs, targets, indices, num_objs, loss_extra_param):\n        has_ann = targets['has_ann'] # b t\n        src_masks = outputs['pred_masks'].permute(0, 2, 1, 3, 4).contiguous() # b nq t h w\n        tgt_masks = targets['masks'] # list[n t' h w]\n        # list[nq t' h w] -> n_sigma t' h w\n        src_masks = torch.cat([t[J][:, haosen] for t, (J, _), haosen in zip(src_masks, indices, has_ann)],dim=0) \n        tgt_masks = torch.cat([t[J] for t, (_, J) in zip(tgt_masks, indices)], dim=0)\n        tgt_masks = tgt_masks.to(src_masks)\n        nf = src_masks.shape[1]\n\n        # No need to upsample predictions as we are using normalized coordinates :)\n        # NT x 1 x H x W\n        src_masks = src_masks.flatten(0, 1).unsqueeze(1).contiguous() # nt' 1 h w\n        target_masks = tgt_masks.flatten(0, 1).unsqueeze(1).contiguous() \n\n        with torch.no_grad():\n            # sample point_coords\n            point_coords = get_uncertain_point_coords_with_randomness(\n                src_masks,\n                lambda logits: calculate_uncertainty(logits),\n                loss_extra_param['num_points'],\n                loss_extra_param['oversample_ratio'],\n                loss_extra_param['importance_sample_ratio'],\n            )\n            # get gt labels\n            point_labels = point_sample(\n                target_masks,\n                point_coords,\n                align_corners=False,\n            ).squeeze(1) # nt' s\n\n        point_logits = point_sample(\n            src_masks,\n            point_coords,\n            align_corners=False,\n        ).squeeze(1) # nt' s\n\n        # point_logits = rearrange(point_logits, '(n t) s -> n (t s)',t=nf)\n        # point_labels = rearrange(point_labels, '(n t) s -> n (t s)',t=nf)\n\n        losses = {\n            \"mask_dice\": ce_mask_loss(point_logits, point_labels, num_objs),\n            \"mask_ce\": dice_loss(point_logits, point_labels, num_objs),\n        }\n\n        del src_masks\n        del target_masks\n        return losses        \n\n    def loss_class_ce(self, outputs, targets, indices, num_objs, loss_extra_param):\n        \"\"\"Classification loss (NLL)\n        targets dicts must contain the key \"labels\" containing a tensor of dim [nb_target_boxes]\n        \"\"\"\n        src_logits = outputs[\"pred_class\"].float() # b nq c\n\n        idx = self._get_src_permutation_idx(indices)\n        # list[n], b -> bn\n        target_classes_o = torch.cat([t[J] for t, (_, J) in zip(targets['classes'], indices)]) \n        target_classes = torch.full(\n            src_logits.shape[:2], self.num_classes, dtype=torch.int64, device=self.device\n        )\n        target_classes[idx] = target_classes_o\n\n        loss_ce = F.cross_entropy(src_logits.transpose(1, 2), target_classes, self.empty_weight)\n        losses = {\"class_ce\": loss_ce}\n        return losses\n\n    def loss_box_l1_giou(self, outputs, targets, indices, num_objs, loss_extra_param): \n        tgt_boxes = targets['boxes'] # list[n tl 4], b\n        has_ann = targets['has_ann'] # b t\n\n        src_boxes = outputs['pred_boxes'].sigmoid().permute(0, 2, 1, 3).contiguous() # b nq t 4\n\n        src_boxes = torch.cat([t[J][:, haosen] for t, (J, _), haosen in zip(src_boxes, indices, has_ann)], dim=0) # n_sum t' 4\n        tgt_boxes = torch.cat([t[J] for t, (_, J) in zip(tgt_boxes, indices)], dim=0) # n_sum t' 4\n        \n        nf = tgt_boxes.shape[1]\n        loss_l1 = F.l1_loss(src_boxes, tgt_boxes, reduction='none').flatten(1) # n_sum t'4\n\n        loss_giou = 1 - torch.diag(box_ops.generalized_box_iou(\n                                    box_ops.box_cxcywh_to_xyxy(src_boxes.flatten(0, 1)),\n                                    box_ops.box_cxcywh_to_xyxy(tgt_boxes.flatten(0, 1)))) # n_sumt'\n        \n        loss_giou = loss_giou.view(-1, nf).contiguous()\n        return {\n            'box_l1': loss_l1.sum(-1).sum() / num_objs,\n            'box_giou': loss_giou.sum(-1).sum() / num_objs\n        }\n\n    def _get_src_permutation_idx(self, indices):\n        # permute predictions following indices\n        batch_idx = torch.cat([torch.full_like(src, i) for i, (src, _) in enumerate(indices)])\n        src_idx = torch.cat([src for (src, _) in indices])\n        return batch_idx, src_idx\n\n    def _get_tgt_permutation_idx(self, indices):\n        # permute targets following indices\n        batch_idx = torch.cat([torch.full_like(tgt, i) for i, (_, tgt) in enumerate(indices)])\n        tgt_idx = torch.cat([tgt for (_, tgt) in indices])\n        return batch_idx, tgt_idx\n\n@META_ARCH_REGISTRY.register()\nclass Video_MaskedAttn_MultiscaleMaskDecoder_v3(nn.Module):\n    def __init__(self,\n                 configs,\n                 multiscale_shapes):\n        super().__init__()\n        d_model = configs['d_model']\n        attn_configs = configs['attn']\n        self.video_nqueries = configs['video_nqueries']\n        self.nlayers = configs['nlayers']\n        self.memory_scales = configs['memory_scales']\n        self.mask_scale = configs['mask_scale']\n        self.mask_spatial_stride = multiscale_shapes[self.mask_scale].spatial_stride\n        num_classes = configs['num_classes']\n\n        inputs_projs = configs['inputs_projs']\n        self.inputs_projs = nn.Sequential()\n        if inputs_projs is not None:\n            self.inputs_projs = META_ARCH_REGISTRY.get(inputs_projs['name'])(inputs_projs, \n                                                                             multiscale_shapes=multiscale_shapes,\n                                                                             out_dim=d_model)\n        self.level_embeds = nn.Embedding(len(self.memory_scales), d_model)\n        assert self.nlayers % len(self.memory_scales) == 0\n        self.cross_layers = _get_clones(CrossAttentionLayer(d_model=d_model,\n                                                            nhead=attn_configs['nheads'],\n                                                            dropout=0.0,\n                                                            normalize_before=attn_configs['normalize_before']),\n                                        self.nlayers)\n        self.self_layers = _get_clones(SelfAttentionLayer(d_model=d_model,\n                                                          nhead=attn_configs['nheads'],\n                                                          dropout=0.0,\n                                                          normalize_before=attn_configs['normalize_before']),\n                                       self.nlayers)  \n        self.ffn_layers = _get_clones(FFNLayer(d_model=d_model,\n                                               dim_feedforward=attn_configs['dim_feedforward'],\n                                               dropout=0.0,\n                                               normalize_before=attn_configs['normalize_before']),\n                                      self.nlayers) \n                  \n        self.nheads = attn_configs['nheads']\n        self.temporal_query_poses = nn.Embedding(self.video_nqueries, d_model)\n        self.temporal_query_feats = nn.Embedding(self.video_nqueries, d_model)\n        self.temporal_query_norm = nn.LayerNorm(d_model)\n        self.pos_3d = build_position_encoding(hidden_dim=d_model, position_embedding_name='3d') # b t c h w\n        \n        self.head_outputs = configs['head_outputs']\n        assert 'mask' in self.head_outputs\n        self.query_mask = MLP(d_model, d_model, d_model, 3)\n        if 'class' in self.head_outputs:\n            self.query_class = nn.Linear(d_model, num_classes + 1)    \n\n        self.loss_module = Video_SetMatchingLoss(loss_config=configs['loss'], num_classes=num_classes)\n        \n    @property\n    def device(self,):\n        return self.temporal_query_feats.weight.device\n\n    def get_memories_and_mask_features(self, multiscales):\n        # b c t h w \n        memories = [multiscales[scale] for scale in self.memory_scales]\n        size_list = [mem_feat.shape[-2:] for mem_feat in memories]\n        memories_poses = [self.pos_3d(mem.permute(0, 2, 1,3, 4)).permute(0, 2, 1, 3, 4) for mem in memories]  # b c t h w\n        memories = [rearrange(mem, 'b c t h w -> (t h w) b c').contiguous() for mem in memories]\n        memories_poses = [rearrange(mem_pos, 'b c t h w -> (t h w) b c').contiguous() for mem_pos in memories_poses]\n        mask_features = multiscales[self.mask_scale] # b c t h w\n        return memories, memories_poses, mask_features, size_list\n\n    def forward(self, \n                multiscales, # b c t h w\n                video_aux_dict=None\n                ):\n        multiscales = self.inputs_projs(multiscales[0])\n        # thw b c; b c t h w\n        memories, memories_poses, mask_features, size_list = self.get_memories_and_mask_features(multiscales)\n        memories = [mem_feat + self.level_embeds.weight[i][None, None, :] for i, mem_feat in enumerate(memories)]\n        batch_size, _, nf, *_ = mask_features.shape\n        \n        # nq b c\n        temporal_query_poses = self.temporal_query_poses.weight.unsqueeze(1).repeat(1, batch_size, 1)\n        temporal_query_feats = self.temporal_query_feats.weight.unsqueeze(1).repeat(1, batch_size, 1)\n\n        vid_ret = []\n        # b nq class, b nq t h w; b*head nq thw\n        vid_class, vid_mask, attn_mask = \\\n            self.forward_heads(temporal_query_feats=temporal_query_feats,\n                               mask_features=mask_features, attn_mask_target_size=size_list[0]) # first sight you re not human\n        vid_ret.append({'pred_class':vid_class, 'pred_masks': vid_mask})\n\n        for i in range(self.nlayers):\n            level_index = i % len(self.memory_scales)\n            attn_mask[torch.where(attn_mask.sum(-1) == attn_mask.shape[-1])] = False  # 全masked掉的 全注意, 比如有padding\n\n            temporal_query_feats = self.cross_layers[i](\n                tgt=temporal_query_feats, # nq b c\n                memory=memories[level_index],  # thw b c\n                memory_mask=attn_mask,  # b*h nq thw\n                memory_key_padding_mask=None,\n                pos=memories_poses[level_index], # thw b c\n                query_pos=temporal_query_poses, # nq b c\n            )\n            temporal_query_feats = self.self_layers[i](\n                temporal_query_feats,\n                tgt_mask=None,\n                tgt_key_padding_mask=None,\n                query_pos=temporal_query_poses,\n            )\n            temporal_query_feats = self.ffn_layers[i](\n                temporal_query_feats \n            )\n            # b nq class, b nq t h w\n            vid_class, vid_mask, attn_mask = \\\n                self.forward_heads(temporal_query_feats=temporal_query_feats,\n                                  mask_features=mask_features, attn_mask_target_size=size_list[(i + 1) % len(self.memory_scales)]) # first sight you re not human\n            vid_ret.append({'pred_class':vid_class, 'pred_masks': vid_mask})\n        \n        return vid_ret\n\n    def forward_heads(self, temporal_query_feats,  mask_features, attn_mask_target_size): # nq b c; b c t h w\n        batch_size, _, nf, *_ = mask_features.shape\n\n        temporal_query_feats = self.temporal_query_norm(temporal_query_feats) # nq b c\n        temporal_query_feats = temporal_query_feats.transpose(0, 1).contiguous() # b nq c\n\n        class_logits = self.query_class(temporal_query_feats) if 'class' in self.head_outputs else None # b n class+1\n        mask_embeds = self.query_mask(temporal_query_feats)  # b n c\n        mask_logits = torch.einsum(\"bqc,bcthw->bqthw\", mask_embeds, mask_features) \n        batch_size, nq, nf = mask_logits.shape[:3]\n        mask_logits = F.interpolate(mask_logits.flatten(0, 1), scale_factor=self.mask_spatial_stride, mode='bilinear', align_corners=False)\n        mask_logits = rearrange(mask_logits, '(b n) t h w -> b t n h w',b=batch_size, n=nq)\n\n        # bt nq h w\n        attn_mask = mask_logits.detach().clone().flatten(0, 1)\n        attn_mask = (F.interpolate(attn_mask, size=attn_mask_target_size, mode=\"bilinear\", align_corners=False) < 0.5).bool()\n        attn_mask = repeat(attn_mask, '(b t) nq h w -> (b head) nq (t h w)', b=batch_size, t=nf, head=self.nheads)\n        \n        if self.training:\n            return class_logits, mask_logits, attn_mask\n        else:\n            return class_logits.softmax(-1).unsqueeze(1).repeat(1, nf, 1, 1) if class_logits is not None else None, mask_logits, attn_mask\n    \n    def compute_loss(self, outputs, targets, video_aux_dict, **kwargs):\n        assert self.training\n        return self.loss_module.compute_loss(model_outs=outputs,\n                                             targets=targets,\n                                             video_aux_dict=video_aux_dict)\n"
  },
  {
    "path": "models/encoder/__init__.py",
    "content": "\nfrom . import localGlobal\nfrom . import input_projs\nfrom . import neighborhood_qk\n"
  },
  {
    "path": "models/encoder/input_projs.py",
    "content": "\nimport torch.nn as nn\nfrom detectron2.modeling import META_ARCH_REGISTRY\nfrom models.layers.anyc_trans import Linear_NormAct\n\nfrom models.layers.anyc_trans import Conv3d_NormAct, Conv2d_NormAct\nfrom einops import rearrange\n\n@META_ARCH_REGISTRY.register()\nclass VideoConv3d_TextLinear(nn.Module):\n    \"\"\"\n    如果multiscale_shapes是None, 那么每个multiscale_shape的input_dim都是out_dim\n    如果multiscale_shapes给出了, 那么按照multiscale shapes里的dim\n    \"\"\"\n    def __init__(self,\n                 configs,\n                 out_dim,\n                 text_dim=None, # 如果是none的话, 那么假设等于out_dim\n                 multiscale_shapes=None, # scale_name: (dim, [temporal_scale, spatial_scale])\n                 ) -> None:\n        super().__init__()\n        text_dim = out_dim if text_dim is None else text_dim\n\n        multiscale_projs_config = configs['video_multiscale_projs']\n        proj_names = multiscale_projs_config.keys() # list[str]\n\n        in_dims = {}\n        if multiscale_shapes is not None:\n            assert set(proj_names).issubset(set(list(multiscale_shapes.keys())))\n            for name in proj_names:\n                in_dims[name] = multiscale_shapes[name].dim\n        else:\n            for name in proj_names:\n                in_dims[name] = out_dim\n\n        projs = {}\n        for name, config in multiscale_projs_config.items():\n            projs[name] = Conv3d_NormAct(in_channels=in_dims[name],\n                                         out_channels=out_dim, \n                                         **config)\n            \n        self.video_multiscale_projs = nn.ModuleDict(projs)\n\n        text_proj_config = configs['text_proj']\n        if text_proj_config is None:\n            self.text_proj = nn.Identity()\n        else:\n            self.text_proj = Linear_NormAct(in_features=text_dim, out_features=out_dim, **text_proj_config)\n\n    def forward(self, multiscales, text_dict):\n        ret = {}\n        for scale_name, scale_feat in multiscales.items(): # b c t h w\n            if scale_name in self.video_multiscale_projs:\n                scale_feat = self.video_multiscale_projs[scale_name](scale_feat)\n                ret[scale_name] = scale_feat\n            else:\n                ret[scale_name] = scale_feat\n        \n        if isinstance(text_dict, AMRData):\n            text_dict.amr_feats = self.text_proj(text_dict.amr_feats)\n            text_dict.text_feats = self.text_proj(text_dict.text_feats)\n        else:\n            raise ValueError()\n        \n        return ret, text_dict\n\n\n\n@META_ARCH_REGISTRY.register()\nclass VideoConv2d_TextLinear(nn.Module):\n    \"\"\"\n    如果multiscale_shapes是None, 那么每个multiscale_shape的input_dim都是out_dim\n    如果multiscale_shapes给出了, 那么按照multiscale shapes里的dim\n    \"\"\"\n    def __init__(self,\n                 configs,\n                 out_dim,\n                 text_dim=None, # 如果是none的话, 那么假设等于out_dim\n                 multiscale_shapes=None, # scale_name: (dim, [temporal_scale, spatial_scale])\n                 ) -> None:\n        super().__init__()\n        text_dim = out_dim if text_dim is None else text_dim\n        multiscale_projs_config = configs['video_multiscale_projs']\n        proj_names = multiscale_projs_config.keys() # list[str]\n\n        in_dims = {}\n        if multiscale_shapes is not None:\n            assert set(proj_names).issubset(set(list(multiscale_shapes.keys())))\n            for name in proj_names:\n                in_dims[name] = multiscale_shapes[name].dim\n        else:\n            for name in proj_names:\n                in_dims[name] = out_dim\n\n        projs = {}\n        for name, config in multiscale_projs_config.items():\n            projs[name] = Conv2d_NormAct(in_channels=in_dims[name],\n                                         out_channels=out_dim, \n                                         **config)\n            \n        self.video_multiscale_projs = nn.ModuleDict(projs)\n\n        text_proj_config = configs['text_proj']\n        if text_proj_config is None:\n            self.text_proj = nn.Identity()\n        else:\n            self.text_proj = Linear_NormAct(in_features=text_dim, out_features=out_dim, **text_proj_config)\n\n    def forward(self, multiscales, text_dict):\n        ret = {}\n        for scale_name, scale_feat in multiscales.items(): # b c t h w\n            if scale_name in self.video_multiscale_projs:\n                batch_size, _, nf = scale_feat.shape[:3]\n                scale_feat = rearrange(scale_feat, 'b c t h w -> (b t) c h w')\n                scale_feat = self.video_multiscale_projs[scale_name](scale_feat)\n                scale_feat = rearrange(scale_feat, '(b t) c h w -> b c t h w', b=batch_size, t=nf)\n                ret[scale_name] = scale_feat\n            else:\n                ret[scale_name] = scale_feat\n        \n        if isinstance(text_dict, AMRData):\n            text_dict.amr_feats = self.text_proj(text_dict.amr_feats)\n            text_dict.text_feats = self.text_proj(text_dict.text_feats)\n        else:\n            raise ValueError()\n        \n        return ret, text_dict\n\n\n@META_ARCH_REGISTRY.register()\nclass ImageConv_MultiscaleProj(nn.Module):\n    def __init__(self,\n                 configs,\n                 out_dim,\n                 multiscale_shapes=None,\n                 ) -> None:\n        \"\"\"\n        如果multiscale_shape是空, 那么输入的dim = out_dim\n        \"\"\"\n        super().__init__()\n        projs_configs = configs['projs']\n        proj_names = list(projs_configs.keys()) # list[str]\n\n        in_dims = {}\n        if multiscale_shapes is not None:\n            assert set(proj_names).issubset(set(list(multiscale_shapes.keys())))\n            for name in proj_names:\n                in_dims[name] = multiscale_shapes[name].dim\n        else:\n            for name in proj_names:\n                in_dims[name] = out_dim\n\n        projs = {}\n        for name, config in projs_configs.items():\n            projs[name] = Conv2d_NormAct(in_channels=in_dims[name], out_channels=out_dim, \n                                         **config)\n        self.multiscale_projs = nn.ModuleDict(projs)\n\n    def forward(self, multiscales):\n        ret = {}\n        for scale_name, scale_feat in multiscales.items():\n            if scale_name in self.multiscale_projs:\n                scale_feat = self.multiscale_projs[scale_name](scale_feat)\n                ret[scale_name] = scale_feat\n            else:\n                ret[scale_name] = scale_feat\n        return ret\n\n\n@META_ARCH_REGISTRY.register()\nclass Video2D_ImageConv_MultiscaleProj(nn.Module):\n    def __init__(self,\n                 configs,\n                 out_dim,\n                 multiscale_shapes=None, # scale_name: (dim, [temporal_scale, spatial_scale])\n                 ) -> None:\n        super().__init__()\n        self.image_homo = ImageConv_MultiscaleProj(configs=configs, out_dim=out_dim, multiscale_shapes=multiscale_shapes)\n    \n    def forward(self, multiscales):\n        batch_sisze, _, nf = multiscales[list(multiscales.keys())[0]].shape[:3]\n        # b c t h w -> bt c h w\n        multiscales = {key: value.permute(0, 2, 1, 3, 4).flatten(0, 1).contiguous() for key,value in multiscales.items()}\n        multiscales = self.image_homo(multiscales)\n        multiscales = {key: rearrange(value, '(b t) c h w -> b c t h w',b=batch_sisze, t=nf).contiguous()\\\n                        for key,value in multiscales.items()}\n        return multiscales        \n\n@META_ARCH_REGISTRY.register()\nclass VideoConv_MultiscaleProj(nn.Module):\n    def __init__(self,\n                 configs,\n                 out_dim,\n                 multiscale_shapes=None,\n                 ) -> None:\n        \"\"\"\n        如果multiscale_shape是空, 那么输入的dim = out_dim\n        \"\"\"\n        super().__init__()\n        projs_configs = configs['projs']\n        proj_names = list(projs_configs.keys()) # list[str]\n\n        in_dims = {}\n        if multiscale_shapes is not None:\n            assert set(proj_names).issubset(set(list(multiscale_shapes.keys())))\n            for name in proj_names:\n                in_dims[name] = multiscale_shapes[name].dim\n        else:\n            for name in proj_names:\n                in_dims[name] = out_dim\n\n        projs = {}\n        for name, config in projs_configs.items():\n            projs[name] = Conv3d_NormAct(in_channels=in_dims[name], out_channels=out_dim, \n                                         **config)\n        self.multiscale_projs = nn.ModuleDict(projs)\n\n    def forward(self, multiscales):\n        ret = {}\n        for scale_name, scale_feat in multiscales.items():\n            if scale_name in self.multiscale_projs:\n                scale_feat = self.multiscale_projs[scale_name](scale_feat)\n                ret[scale_name] = scale_feat\n            else:\n                ret[scale_name] = scale_feat\n        return ret\n\n     \n\n\n\n\n@META_ARCH_REGISTRY.register()\nclass FrameQueryLinear_TextLinear(nn.Module):\n    def __init__(self,\n                 configs,\n                 out_dim,\n                 text_dim=None, # int\n                 query_dim=None, # scale_name: (dim, [temporal_scale, spatial_scale])\n                 ) -> None:\n        super().__init__()\n        query_proj_config = configs['query_proj']\n        query_dim = out_dim if query_dim is None else query_dim\n        text_dim = out_dim if text_dim is None else text_dim\n\n        self.query_proj = Linear_NormAct(in_features=query_dim, out_features=out_dim, **query_proj_config)\n        text_proj_config = configs['text_proj']\n        if text_proj_config is None:\n            self.text_proj = nn.Identity()\n        else:\n            self.text_proj = Linear_NormAct(in_features=text_dim, out_features=out_dim, **text_proj_config)\n\n    def forward(self, frame_query, text_dict):\n        # b T nqf c\n        # text_dict\n        frame_query = self.query_proj(frame_query)\n\n        if isinstance(text_dict, AMRData):\n            text_dict.amr_feats = self.text_proj(text_dict.amr_feats)\n            text_dict.text_feats = self.text_proj(text_dict.text_feats)\n        else:\n            raise ValueError()\n        \n        return frame_query, text_dict\n\n\n@META_ARCH_REGISTRY.register()\nclass VideoConv3d_FrameQueryLinear_TextLinear(nn.Module):\n    def __init__(self,\n                 configs,\n                 out_dim,\n                 feat_dim=None,\n                 text_dim=None, # int\n                 query_dim=None, # scale_name: (dim, [temporal_scale, spatial_scale])\n                 ) -> None:\n        super().__init__()\n        query_proj_config = configs['query_proj']\n        feat_proj_config = configs['feat_proj']\n        text_proj_config = configs['text_proj']\n\n        feat_dim = out_dim if feat_dim is None else feat_dim\n        query_dim = out_dim if query_dim is None else query_dim\n        text_dim = out_dim if text_dim is None else text_dim\n\n        self.query_proj = Linear_NormAct(in_features=query_dim, out_features=out_dim, **query_proj_config) if query_proj_config is not None else nn.Identity()\n        self.text_proj = Linear_NormAct(in_features=text_dim, out_features=out_dim, **text_proj_config) if text_proj_config is not None else nn.Identity()\n        self.feat_proj = Conv3d_NormAct(in_channels=feat_dim, out_channels=out_dim, **feat_proj_config)\n\n    def forward(self, mask_feat, frame_query, text_dict):\n        mask_feat = self.feat_proj(mask_feat)\n        frame_query = self.query_proj(frame_query)\n        if isinstance(text_dict, AMRData):\n            text_dict.amr_feats = self.text_proj(text_dict.amr_feats)\n            text_dict.text_feats = self.text_proj(text_dict.text_feats)\n        else:\n            raise ValueError()\n\n        return mask_feat, frame_query, text_dict\n\n# 每一个module应该都把input进行一边proj, proj到自己的空间里\n@META_ARCH_REGISTRY.register()\nclass VideoConv3d_FrameQueryLinear(nn.Module):\n    \"\"\"\n    如果multiscale_shapes是None, 那么每个multiscale_shape的input_dim都是out_dim\n    如果multiscale_shapes给出了, 那么按照multiscale shapes里的dim\n    \"\"\"\n    def __init__(self,\n                 configs,\n                 out_dim,\n                 query_dim=None, # 如果是none的话, 那么假设等于out_dim\n                 multiscale_shapes=None, # scale_name: (dim, [temporal_scale, spatial_scale])\n                 ) -> None:\n        super().__init__()\n        query_dim = out_dim if query_dim is None else query_dim\n\n        multiscale_projs_config = configs['video_multiscale_projs']\n        proj_names = multiscale_projs_config.keys() # list[str]\n\n        in_dims = {}\n        if multiscale_shapes is not None:\n            assert set(proj_names).issubset(set(list(multiscale_shapes.keys())))\n            for name in proj_names:\n                in_dims[name] = multiscale_shapes[name].dim\n        else:\n            for name in proj_names:\n                in_dims[name] = out_dim\n\n        projs = {}\n        for name, config in multiscale_projs_config.items():\n            projs[name] = Conv3d_NormAct(in_channels=in_dims[name],\n                                         out_channels=out_dim, \n                                         **config)\n            \n        self.video_multiscale_projs = nn.ModuleDict(projs)\n\n        query_proj_config = configs['query_proj']\n        if query_proj_config is None:\n            self.query_proj = nn.Identity()\n        else:\n            self.query_proj = Linear_NormAct(in_features=query_dim, out_features=out_dim, **query_proj_config)\n\n    def forward(self, multiscales, frame_queries):\n        # b t nq c\n        ret = {}\n        for scale_name, scale_feat in multiscales.items(): # b c t h w\n            if scale_name in self.video_multiscale_projs:\n                scale_feat = self.video_multiscale_projs[scale_name](scale_feat)\n                ret[scale_name] = scale_feat\n            else:\n                ret[scale_name] = scale_feat\n        \n        frame_queries = self.query_proj(frame_queries)\n        return ret, frame_queries\n\n\n\n@META_ARCH_REGISTRY.register()\nclass FrameQueryLinear(nn.Module):\n    def __init__(self,\n                 configs,\n                 out_dim,\n                 query_dim=None, # scale_name: (dim, [temporal_scale, spatial_scale])\n                 ) -> None:\n        super().__init__()\n        query_proj_config = configs['query_proj']\n        query_dim = out_dim if query_dim is None else query_dim\n\n        self.query_proj = Linear_NormAct(in_features=query_dim, out_features=out_dim, **query_proj_config)\n\n    def forward(self, frame_query):\n        # b T nqf c\n        frame_query = self.query_proj(frame_query)\n        \n        return frame_query\n\n"
  },
  {
    "path": "models/encoder/localGlobal.py",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates.\nimport logging\nimport numpy as np\nfrom typing import Callable, Dict, List, Optional, Tuple, Union\n\nimport fvcore.nn.weight_init as weight_init\nimport torch\nfrom torch import nn\nfrom torch.nn import functional as F\nfrom torch.nn.init import xavier_uniform_, constant_, uniform_, normal_\nfrom torch.cuda.amp import autocast\n\nfrom detectron2.config import configurable\nfrom detectron2.layers import Conv2d, ShapeSpec, get_norm\nfrom detectron2.modeling import META_ARCH_REGISTRY\nfrom models.layers.position_encoding import PositionEmbeddingSine\nfrom models.layers.utils import _get_clones, _get_activation_fn\nfrom .ops.modules import MSDeformAttn\n\n# MSDeformAttn Transformer encoder in deformable detr\nclass MSDeformAttnTransformerEncoderOnly(nn.Module):\n    def __init__(self, d_model=256, nhead=8,\n                 num_encoder_layers=6, dim_feedforward=1024, dropout=0.1,\n                 activation=\"relu\",\n                 num_feature_levels=4, enc_n_points=4,\n                 add_local = False,\n                 add_global=False,\n                 local_configs=None,\n                 global_configs=None,\n                 frame_nqueries=None,\n        ):\n        super().__init__()\n\n        self.d_model = d_model\n        self.nhead = nhead\n\n        encoder_layer = MSDeformAttnTransformerEncoderLayer(d_model = d_model,\n                                                            d_ffn = dim_feedforward,\n                                                            dropout = dropout,\n                                                            activation = activation,\n                                                            n_levels = num_feature_levels,\n                                                            n_heads = nhead,\n                                                            n_points = enc_n_points,\n                                                            add_local = add_local,\n                                                            add_global = add_global,\n                                                            local_configs = local_configs,\n                                                            global_configs = global_configs\n                                                            )\n        self.encoder = MSDeformAttnTransformerEncoder(encoder_layer, num_encoder_layers,\n                                                      d_model=d_model,\n                                                      frame_nqueries=frame_nqueries)\n\n        self.level_embed = nn.Parameter(torch.Tensor(num_feature_levels, d_model))\n\n        self._reset_parameters()\n\n    def _reset_parameters(self):\n        for p in self.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_uniform_(p)\n        for m in self.modules():\n            if isinstance(m, MSDeformAttn):\n                m._reset_parameters()\n        normal_(self.level_embed)\n\n    def get_valid_ratio(self, mask):\n        _, H, W = mask.shape # b h w\n        valid_H = torch.sum(~mask[:, :, 0], 1) # b \n        valid_W = torch.sum(~mask[:, 0, :], 1) # b\n        valid_ratio_h = valid_H.float() / H \n        valid_ratio_w = valid_W.float() / W\n        valid_ratio = torch.stack([valid_ratio_w, valid_ratio_h], -1) # b 2\n        return valid_ratio\n\n    def forward(self, \n                srcs=None, \n                pos_embeds=None,\n                video_aux_dict=None,\n                **kwargs):\n        masks = [torch.zeros((x.size(0), x.size(2), x.size(3)), device=x.device, dtype=torch.bool) for x in srcs]\n        # prepare input for encoder\n        src_flatten = []\n        mask_flatten = []\n        lvl_pos_embed_flatten = []\n        spatial_shapes = []\n        for lvl, (src, mask, pos_embed) in enumerate(zip(srcs, masks, pos_embeds)):\n            bs, c, h, w = src.shape\n            spatial_shape = (h, w)\n            spatial_shapes.append(spatial_shape)\n            src = src.flatten(2).transpose(1, 2)\n            mask = mask.flatten(1)\n            pos_embed = pos_embed.flatten(2).transpose(1, 2)\n            lvl_pos_embed = pos_embed + self.level_embed[lvl].view(1, 1, -1)\n            lvl_pos_embed_flatten.append(lvl_pos_embed)\n            src_flatten.append(src)\n            mask_flatten.append(mask)\n        src_flatten = torch.cat(src_flatten, 1)\n        mask_flatten = torch.cat(mask_flatten, 1)\n        lvl_pos_embed_flatten = torch.cat(lvl_pos_embed_flatten, 1)\n        spatial_shapes = torch.as_tensor(spatial_shapes, dtype=torch.long, device=src_flatten.device)\n        level_start_index = torch.cat((spatial_shapes.new_zeros((1, )), spatial_shapes.prod(1).cumsum(0)[:-1]))\n        valid_ratios = torch.stack([self.get_valid_ratio(m) for m in masks], 1) # b #scale 2\n\n        # encoder\n        memory, frame_feats, frame_poses = self.encoder(src=src_flatten,  # bt hw_sigma c\n                                                        spatial_shapes=spatial_shapes, \n                                                        level_start_index=level_start_index, \n                                                        valid_ratios=valid_ratios, \n                                                        pos=lvl_pos_embed_flatten, \n                                                        padding_mask=mask_flatten,\n                                                        video_aux_dict=video_aux_dict)\n\n        return memory, spatial_shapes, level_start_index, frame_feats, frame_poses\n\nclass MSDeformAttnTransformerEncoderLayer(nn.Module):\n    def __init__(self,\n                 d_model=256, d_ffn=1024,\n                 dropout=0.1, activation=\"relu\",\n                 n_levels=4, n_heads=8, n_points=4,\n                 add_local=False,\n                 add_global=False,\n                 local_configs=None,\n                 global_configs=None):\n        super().__init__()\n        # deform2d\n        self.self_attn = MSDeformAttn(d_model, n_levels, n_heads, n_points)\n        self.dropout1 = nn.Dropout(dropout)\n        self.norm1 = nn.LayerNorm(d_model)\n\n        self.add_local = add_local\n        if self.add_local:\n            from .neighborhood_qk import NA_qk_Layer\n            # self\n            self.local_cnp = NA_qk_Layer(d_model=d_model, configs=local_configs)\n\n\n        self.add_global = add_global\n        if self.add_global:\n            from models.layers.decoder_layers import CrossAttentionLayer \n            # cross\n            self.frame_query_cross_multiscale = CrossAttentionLayer(d_model=d_model, nhead=8, dropout=0.0,\n                                                                    activation=\"relu\", normalize_before=False)\n            self.cross_num_heads = 8\n            self.global_add_attn_mask = global_configs['add_attn_mask'] if 'add_attn_mask' in global_configs else False\n            # self+ffn\n            from models.encoder.ops.modules.frame_query_ss2d import FrameQuery_SS2DLayer_hilbert \n            self.global_hiss = FrameQuery_SS2DLayer_hilbert(global_configs)\n\n            self.multiscale_cross_query = CrossAttentionLayer(d_model=d_model, nhead=8, dropout=0.0,\n                                                              activation=\"relu\", normalize_before=False)\n        # ffn\n        self.linear1 = nn.Linear(d_model, d_ffn)\n        self.activation = _get_activation_fn(activation)\n        self.dropout2 = nn.Dropout(dropout)\n        self.linear2 = nn.Linear(d_ffn, d_model)\n        self.dropout3 = nn.Dropout(dropout)\n        self.norm2 = nn.LayerNorm(d_model)\n\n    @staticmethod\n    def with_pos_embed(tensor, pos):\n        return tensor if pos is None else tensor + pos\n\n    def forward_ffn(self, src):\n        src2 = self.linear2(self.dropout2(self.activation(self.linear1(src))))\n        src = src + self.dropout3(src2)\n        src = self.norm2(src)\n        return src\n\n    @torch.no_grad()\n    def get_attn_mask(self, frame_query_feats, src, spatial_shapes, level_start_index,):\n        # nq bt c\n        # bt hw_sigma c\n        assert len(spatial_shapes) == 3\n        frame_query_feats = frame_query_feats.permute(1, 0, 2) # bt nq c\n        feat = src[:, level_start_index[-1]: (level_start_index[-1] + spatial_shapes[-1][0] * spatial_shapes[-1][1])]\n        feat = rearrange(feat, 'b (h w) c -> b c h w',h=spatial_shapes[-1][0],w=spatial_shapes[-1][1])\n        mask = torch.einsum('bnc, bchw -> b n h w',frame_query_feats, feat)\n        mask_2 = F.interpolate(mask, size=spatial_shapes[0].tolist(), mode='bilinear',align_corners=False)\n        mask_3 = F.interpolate(mask, size=spatial_shapes[1].tolist(), mode='bilinear', align_corners=False)\n        attn_mask = torch.cat([mask_2.flatten(2), mask_3.flatten(2), mask.flatten(2)], dim=-1) #bt n hw_sigma\n        attn_mask = (attn_mask.unsqueeze(1).repeat(1, self.cross_num_heads, 1, 1).flatten(0, 1).sigmoid() < 0.5).bool()\n        return attn_mask\n\n    \n    def forward(self, \n                src=None, pos=None, \n                reference_points=None, spatial_shapes=None, level_start_index=None, padding_mask=None,\n                video_aux_dict=None,\n                frame_query_feats=None, # nq bt c\n                frame_query_poses=None):\n        \n        if self.add_local:\n            # local_self\n            src = self.local_cnp(tgt=src, \n                                scale_shapes=spatial_shapes,\n                                level_start_idxs=level_start_index,\n                                nf=video_aux_dict['nf']) \n        if self.add_global:\n            if self.global_add_attn_mask:\n                attn_mask = self.get_attn_mask(frame_query_feats, src, spatial_shapes, level_start_index,) # bthead nq hw\n                attn_mask[torch.where(attn_mask.sum(-1) == attn_mask.shape[-1])] = False  # 全masked掉的 全注意, 比如有padding\n            else:\n                attn_mask = None\n            # cross\n            frame_query_feats = self.frame_query_cross_multiscale(\n                tgt=frame_query_feats,  # nq bt c\n                memory=src.permute(1, 0, 2), # hw_sigma bt c\n                memory_mask=attn_mask,\n                memory_key_padding_mask=None,\n                pos= pos.permute(1,0,2),\n                query_pos=frame_query_poses,\n            )\n            # self+ffn\n            frame_query_feats = self.global_hiss(frame_query_feats=frame_query_feats,\n                                                 frame_query_poses=frame_query_poses,\n                                                 video_aux_dict=video_aux_dict)\n\n            # self\n            src = self.multiscale_cross_query(\n                tgt=src.permute(1, 0, 2),  # hw_sigma bt c\n                memory=frame_query_feats, # nq bt c\n                memory_mask=None,\n                memory_key_padding_mask=None,\n                pos= frame_query_poses,\n                query_pos=pos.permute(1,0,2),                \n            ).permute(1, 0, 2)\n\n        # self attention\n        src2 = self.self_attn(self.with_pos_embed(src, pos), reference_points, src, spatial_shapes, level_start_index, padding_mask)[0]\n        src = src + self.dropout1(src2)\n        src = self.norm1(src)\n\n        # ffn\n        src = self.forward_ffn(src)\n        return src, frame_query_feats\n\n\nclass MSDeformAttnTransformerEncoder(nn.Module):\n    def __init__(self,\n                encoder_layer=None, \n                num_layers=None, \n                d_model=None, frame_nqueries=None):\n        super().__init__()\n        self.layers = _get_clones(encoder_layer, num_layers)\n        self.num_layers = num_layers\n\n        self.frame_nqueries = frame_nqueries # 10\n        self.frame_query_feats = nn.Embedding(self.frame_nqueries, d_model)\n        self.frame_query_poses = nn.Embedding(self.frame_nqueries, d_model)\n\n    @staticmethod\n    def get_reference_points(spatial_shapes, valid_ratios, device):\n        # b #scale 2, valid_w(0-1), valid_h(0-1), 整个feature map有多少是非padding的\n        # list[h w] #scale\n        reference_points_list = []\n        for lvl, (H_, W_) in enumerate(spatial_shapes):\n            ref_y, ref_x = torch.meshgrid(torch.linspace(0.5, H_ - 0.5, H_, dtype=torch.float32, device=device),\n                                          torch.linspace(0.5, W_ - 0.5, W_, dtype=torch.float32, device=device))\n            ref_y = ref_y.reshape(-1)[None] / (valid_ratios[:, None, lvl, 1] * H_) # 1 hw / b 1 -> b hw(0-1), y的绝对坐标\n            ref_x = ref_x.reshape(-1)[None] / (valid_ratios[:, None, lvl, 0] * W_) # 1 hw / b 1 -> b hw(0-1), x的绝对坐标\n            ref = torch.stack((ref_x, ref_y), -1) # b hw 2\n            reference_points_list.append(ref)\n        reference_points = torch.cat(reference_points_list, 1) # b hw_sigma 2, 每个点的相对坐标(0-1)\n        reference_points = reference_points[:, :, None] * valid_ratios[:, None] # b hw_sigma 1 2 * b 1 #scale 2\n        return reference_points # b hw_sigma #scale 2\n\n    def forward(self, \n                src,  # bt hw_sigma c\n                spatial_shapes, \n                level_start_index, \n                valid_ratios, \n                pos=None, \n                padding_mask=None,\n                video_aux_dict=None):\n        \n        output = src # bt hw_sigma c\n        batch_size_nf = output.shape[0]\n        frame_query_feats = self.frame_query_feats.weight.unsqueeze(1).repeat(1,batch_size_nf, 1)\n        frame_query_poses = self.frame_query_poses.weight.unsqueeze(1).repeat(1,batch_size_nf,1) # n bt c\n        \n        reference_points = self.get_reference_points(spatial_shapes, valid_ratios, device=src.device)\n        frame_feats = []\n        for _, layer in enumerate(self.layers):\n            output, frame_query_feats = layer(src=output, \n                                                pos=pos, \n                                                reference_points=reference_points, \n                                                spatial_shapes=spatial_shapes, \n                                                level_start_index=level_start_index, \n                                                padding_mask=padding_mask,\n                                                video_aux_dict=video_aux_dict,\n                                                frame_query_feats=frame_query_feats,\n                                                frame_query_poses=frame_query_poses)\n            frame_feats.append(frame_query_feats)\n\n        return output, frame_feats, frame_query_poses\n\n\nimport copy\nfrom einops import rearrange\nfrom models.layers.utils import _get_clones\nfrom models.layers.position_encoding import build_position_encoding\n# video multiscale, text_dict\n\n@META_ARCH_REGISTRY.register()\nclass Video_Deform2D_DividedTemporal_MultiscaleEncoder_localGlobal(nn.Module):\n    def __init__(\n        self,\n        configs,\n        multiscale_shapes, # {'res2': .temporal_stride, .spatial_stride, .dim}\n    ):\n        super().__init__()\n        d_model = configs['d_model']\n        fpn_norm = configs['fpn_norm'] # fpn的norm\n        nlayers = configs['nlayers']\n\n        # 4, 8, 16, 32\n        self.multiscale_shapes = dict(sorted(copy.deepcopy(multiscale_shapes).items(), key=lambda x: x[1].spatial_stride))\n        self.encoded_scales = sorted(configs['encoded_scales'], \n                                     key=lambda x:self.multiscale_shapes[x].spatial_stride) # res3, res4, res5\n        \n        # 4 -> 8 -> 16 -> 32    \n        self.scale_dims = [val.dim for val in multiscale_shapes.values()]\n        self.video_projs = META_ARCH_REGISTRY.get(configs['video_projs']['name'])(configs=configs['video_projs'],\n                                                                            multiscale_shapes=multiscale_shapes, out_dim=d_model)\n\n        self.pos_2d = build_position_encoding(position_embedding_name='2d')\n\n        deform_attn = configs['deform_attn']\n        self.transformer = MSDeformAttnTransformerEncoderOnly(\n            d_model=d_model,\n            dropout=deform_attn['dropout'],\n            nhead=deform_attn['nheads'],\n            dim_feedforward=deform_attn['dim_feedforward'],\n            activation=deform_attn['activation'],\n            num_encoder_layers=nlayers,\n            num_feature_levels=len(self.encoded_scales),\n            enc_n_points=deform_attn['enc_n_points'],\n            add_local = configs['add_local'],\n            add_global = configs['add_global'],\n            local_configs = configs['local_configs'],\n            global_configs = configs['global_configs'],\n            frame_nqueries=configs['frame_nqueries']\n                \n        )\n\n        min_encode_stride = self.multiscale_shapes[self.encoded_scales[0]].spatial_stride # 8\n        min_stride = list(self.multiscale_shapes.values())[0].spatial_stride # 4\n        self.num_fpn_levels = int(np.log2(min_encode_stride) - np.log2(min_stride))\n        lateral_convs = [] \n        output_convs = []\n        use_bias = fpn_norm == \"\"\n        for idx, in_channels in enumerate(self.scale_dims[:self.num_fpn_levels]):\n            lateral_norm = get_norm(fpn_norm, d_model)\n            output_norm = get_norm(fpn_norm, d_model)\n\n            lateral_conv = Conv2d(in_channels, d_model, kernel_size=1, bias=use_bias, norm=lateral_norm)\n            output_conv = Conv2d(d_model, d_model, kernel_size=3, padding=1, bias=use_bias, norm=output_norm, activation=F.relu)\n\n            self.add_module(\"adapter_{}\".format(idx + 1), lateral_conv)\n            self.add_module(\"layer_{}\".format(idx + 1), output_conv)\n\n            lateral_convs.append(lateral_conv)\n            output_convs.append(output_conv)\n        # Place convs into top-down order (from low to high resolution)\n        # to make the top-down computation in forward clearer.\n        self.lateral_convs = lateral_convs[::-1] # 8 4\n        self.output_convs = output_convs[::-1] # 8 4\n\n    def forward(self, \n                multiscales=None, # b c t h w\n                video_aux_dict=None, # dict{}\n                **kwargs):\n        batch_size, _, nf = multiscales[list(multiscales.keys())[0]].shape[:3]\n        video_aux_dict['nf'] = nf\n        multiscales = self.video_projs(multiscales) \n        assert set(list(multiscales.keys())).issubset(set(list(self.multiscale_shapes.keys())))\n        assert set(list(self.multiscale_shapes.keys())).issubset(set(list(multiscales.keys())))\n\n        srcs = []\n        poses = [] # 32, 16, 8\n        for idx, scale_name in enumerate(self.encoded_scales[::-1]):\n            x = multiscales[scale_name].permute(0, 2, 1, 3, 4).flatten(0,1).contiguous() # bt c h w\n            srcs.append(x)\n            poses.append(self.pos_2d(torch.zeros_like(x)[:, 0, :, :].bool(), hidden_dim=x.shape[1]))\n\n        memory, spatial_shapes, level_start_index, frame_feats, frame_poses = self.transformer(srcs=srcs, \n                                                                                                pos_embeds=poses,\n                                                                                                video_aux_dict=video_aux_dict)\n        bs = memory.shape[0]\n        spatial_index = 0\n        memory_features = [] # 32 16 8\n        for lvl in range(len(self.encoded_scales)):\n            h, w = spatial_shapes[lvl]\n            memory_lvl = memory[:, spatial_index : spatial_index + h * w, :].reshape(bs, h, w, -1).permute(0, 3, 1, 2).contiguous()  \n            memory_features.append(memory_lvl)\n            spatial_index += h * w\n\n        for idx, f in enumerate(list(self.multiscale_shapes.keys())[:self.num_fpn_levels][::-1]):\n            x = multiscales[f].permute(0, 2, 1, 3, 4).flatten(0,1).contiguous() # bt c h w\n            cur_fpn = self.lateral_convs[idx](x)\n            y = cur_fpn + F.interpolate(memory_features[-1], size=cur_fpn.shape[-2:], mode=\"bilinear\", align_corners=False)\n            y = self.output_convs[idx](y)\n            memory_features.append(y)\n\n        assert len(memory_features) == len(list(self.multiscale_shapes.keys()))\n\n        ret = {}\n        for key, out_feat in zip(list(self.multiscale_shapes.keys()), memory_features[::-1]):\n            ret[key] = rearrange(out_feat, '(b t) c h w -> b c t h w', b=batch_size, t=nf)\n        return ret, frame_feats[::-1], frame_poses # 32, 16, 8\n\n"
  },
  {
    "path": "models/encoder/neighborhood_qk.py",
    "content": "from typing import Optional\n\nimport torch\nfrom torch import nn, Tensor\nfrom torch.nn.functional import pad\nfrom torch.nn.init import trunc_normal_\n\nfrom natten.functional import na2d_av, na2d_qk_with_bias\nfrom einops import rearrange\nfrom natten import NeighborhoodAttention2D\nfrom detectron2.modeling import META_ARCH_REGISTRY\n\nclass NeighborhoodAttention2D_qk(nn.Module):\n    \"\"\"\n    Neighborhood Attention 2D Module\n    \"\"\"\n\n    def __init__(\n        self,\n        dim: int,\n        num_heads: int,\n        kernel_size: int,\n        dilation: int = 1,\n        bias: bool = True,\n        qkv_bias: bool = True,\n        qk_scale: Optional[float] = None,\n        attn_drop: float = 0.0,\n        proj_drop: float = 0.0,\n    ):\n        super().__init__()\n        self.num_heads = num_heads\n        self.head_dim = dim // self.num_heads\n        self.scale = qk_scale or self.head_dim**-0.5\n        assert (\n            kernel_size > 1 and kernel_size % 2 == 1\n        ), f\"Kernel size must be an odd number greater than 1, got {kernel_size}.\"\n        self.kernel_size = kernel_size\n        assert (\n            dilation is None or dilation >= 1\n        ), f\"Dilation must be greater than or equal to 1, got {dilation}.\"\n        self.dilation = dilation or 1\n        self.window_size = self.kernel_size * self.dilation\n\n        self.q_linear = nn.Linear(dim, dim, bias=qkv_bias)\n        self.kv_linear = nn.Linear(dim, dim * 2, bias=qkv_bias)\n        if bias:\n            self.rpb = nn.Parameter(\n                torch.zeros(num_heads, (2 * kernel_size - 1), (2 * kernel_size - 1))\n            )\n            trunc_normal_(self.rpb, std=0.02, mean=0.0, a=-2.0, b=2.0)\n        else:\n            self.register_parameter(\"rpb\", None)\n        self.attn_drop = nn.Dropout(attn_drop)\n        self.proj = nn.Linear(dim, dim)\n        self.proj_drop = nn.Dropout(proj_drop)\n\n    def forward(self, \n                x_q: Tensor, \n                x_k: Tensor) -> Tensor:\n        # bt h w c; bt h w c, 前一帧\n        if x_q.dim() != 4:\n            raise ValueError(\n                f\"NeighborhoodAttention2D expected a rank-4 input tensor; got {x.dim()=}.\"\n            )\n\n        B, H, W, C = x_q.shape\n        # Pad if the input is small than the minimum supported size\n        H_padded, W_padded = H, W\n        padding_h = padding_w = 0\n        if H < self.window_size or W < self.window_size:\n            padding_h = max(0, self.window_size - H_padded)\n            padding_w = max(0, self.window_size - W_padded)\n            x_q = pad(x_q, (0, 0, 0, padding_w, 0, padding_h))\n            x_k = pad(x_k, (0, 0, 0, padding_w, 0, padding_h))\n            _, H_padded, W_padded, _ = x_q.shape\n\n        # b h w c -> b h w h c_h\n        q = self.q_linear(x_q).reshape(B, H_padded, W_padded, self.num_heads, self.head_dim)\n        q = q.permute(0, 3, 1, 2, 4) # b head h w c_h\n        kv = (\n            self.kv_linear(x_k)\n            .reshape(B, H_padded, W_padded, 2, self.num_heads, self.head_dim)\n            .permute(3, 0, 4, 1, 2, 5)\n        ) # b \n        k, v = kv[0], kv[1]\n        q = q * self.scale\n        attn = na2d_qk_with_bias(q, k, self.rpb, self.kernel_size, self.dilation)\n        attn = attn.softmax(dim=-1)\n        attn = self.attn_drop(attn)\n        x_q = na2d_av(attn, v, self.kernel_size, self.dilation) # b head h w c_h\n        x_q = x_q.permute(0, 2, 3, 1, 4).reshape(B, H_padded, W_padded, C) # b h w head c_h\n\n        # Remove padding, if added any\n        if padding_h or padding_w:\n            x_q = x_q[:, :H, :W, :].contiguous()\n\n        return self.proj_drop(self.proj(x_q))\n\n    def extra_repr(self) -> str:\n        return (\n            f\"head_dim={self.head_dim}, num_heads={self.num_heads}, \"\n            + f\"kernel_size={self.kernel_size}, dilation={self.dilation}, \"\n            + f\"has_bias={self.rpb is not None}\"\n        )\n\nfrom models.layers.utils import _get_clones\nclass NA_qk_Layer(nn.Module):\n\n    def __init__(self, d_model, configs):\n        super().__init__()\n        self.self_attn = NeighborhoodAttention2D_qk(dim=configs['d_model'],\n                                                    num_heads=configs['num_heads'],\n                                                    kernel_size=configs['kernel_size'],\n                                                    dilation=configs['dilation'],\n                                                    bias=False,\n                                                    qkv_bias=False,)\n        self.num_steps = configs['num_steps'] if 'num_steps' in configs else 1\n        self.norm = nn.LayerNorm(d_model)\n        self.dropout = nn.Dropout(configs['dropout'])\n    \n\n    def forward(self, tgt=None, scale_shapes=None, level_start_idxs=None, nf=None):\n        # bt hw_sigma c -> list[b t h w c], 3\n        video_feats = [tgt[:, start_idx:(start_idx + haosen[0]*haosen[1])].contiguous() for start_idx, haosen in zip(level_start_idxs, scale_shapes)]\n        video_feats = [rearrange(haosen, '(b t) (h w) c -> b t h w c', t=nf, h=scale_shapes[idx][0], w=scale_shapes[idx][1]).contiguous() for idx, haosen in enumerate(video_feats)]\n\n        video_key_feats = []\n        for haosen in video_feats:\n            scale_feats = torch.stack([torch.roll(haosen, shifts=k, dims=1) for k in range(1, self.num_steps+1)], dim=0) # s b t h w c\n            video_key_feats.append(scale_feats.flatten(0, 2)) #sbt h w c\n        \n        # sbt h w c\n        video_feats = [haosen.unsqueeze(0).repeat(self.num_steps, 1,1,1,1,1).flatten(0, 2) for haosen in video_feats]\n \n        local_feats = [] # list[sbt h w c]\n        for idx, (q_feat, k_feat) in enumerate(zip(video_feats, video_key_feats)):\n            local_feats.append(self.self_attn(q_feat, k_feat))\n        local_feats = [rearrange(haosen, '(s bt) h w c -> s bt h w c',s=self.num_steps) for haosen in local_feats]\n        local_feats = [haosen.sum(dim=0) for haosen in local_feats] # bt h w c\n        local_feats = torch.cat([haosen.flatten(1, 2) for haosen in local_feats], dim=1) # bt hw_sigma c\n        tgt = tgt + self.dropout(local_feats)\n        tgt = self.norm(tgt) \n        return tgt\n\n@META_ARCH_REGISTRY.register()\nclass NA_qk_Layer_v2(nn.Module):\n\n    def __init__(self, configs):\n        super().__init__()\n        self.self_attn = NeighborhoodAttention2D_qk(dim=configs['d_model'],\n                                                    num_heads=configs['num_heads'],\n                                                    kernel_size=configs['kernel_size'],\n                                                    dilation=configs['dilation'],\n                                                    bias=False,\n                                                    qkv_bias=False,)\n    \n\n    def forward(self,\n                query=None, \n                spatial_shapes=None,\n                level_start_index=None,\n                video_aux_dict=None,):\n        \n        # bt hw_sigma c -> list[b t h w c], 3\n        video_feat = [query[:, start_idx:(start_idx + haosen[0]*haosen[1])].contiguous() for start_idx, haosen in zip(level_start_index, spatial_shapes)]\n        video_feat = [rearrange(haosen, '(b t) (h w) c -> b t h w c',t=video_aux_dict['nf'], h=spatial_shapes[idx][0], w=spatial_shapes[idx][1]).contiguous() for idx, haosen in enumerate(video_feat)]\n        video_key_feats = [torch.roll(haosen, shifts=1, dims=1).contiguous() for haosen in video_feat]\n\n        local_feats = [] # list[bt h w c]\n        for idx, (q_feat, k_feat) in enumerate(zip(video_feat, video_key_feats)):\n            local_feats.append(self.self_attn(q_feat.flatten(0, 1), k_feat.flatten(0, 1)))\n        \n        local_feats = torch.cat([haosen.flatten(1, 2) for haosen in local_feats], dim=1) # bt hw_sigma c\n        return local_feats, None\n"
  },
  {
    "path": "models/encoder/ops/MultiScaleDeformableAttention.egg-info/PKG-INFO",
    "content": "Metadata-Version: 2.1\nName: MultiScaleDeformableAttention\nVersion: 1.0\nSummary: PyTorch Wrapper for CUDA Functions of Multi-Scale Deformable Attention\nHome-page: https://github.com/fundamentalvision/Deformable-DETR\nAuthor: Weijie Su\n"
  },
  {
    "path": "models/encoder/ops/attention.py",
    "content": "from inspect import isfunction\nimport math\nimport torch\nfrom torch.nn.init import xavier_uniform_, constant_\nimport torch.nn.functional as F\nfrom torch import nn, einsum\nfrom einops import rearrange, repeat\nfrom .functions import MSDeformAttnFunction\nimport warnings\n\ndef exists(val):\n    return val is not None\n\n\ndef uniq(arr):\n    return{el: True for el in arr}.keys()\n\n\ndef default(val, d):\n    if exists(val):\n        return val\n    return d() if isfunction(d) else d\n\n\ndef max_neg_value(t):\n    return -torch.finfo(t.dtype).max\n\n\ndef init_(tensor):\n    dim = tensor.shape[-1]\n    std = 1 / math.sqrt(dim)\n    tensor.uniform_(-std, std)\n    return tensor\n\n\n# feedforward\nclass GEGLU(nn.Module):\n    def __init__(self, dim_in, dim_out):\n        super().__init__()\n        self.proj = nn.Linear(dim_in, dim_out * 2)\n\n    def forward(self, x):\n        x, gate = self.proj(x).chunk(2, dim=-1)\n        return x * F.gelu(gate)\n\n\nclass FeedForward(nn.Module):\n    def __init__(self, dim, dim_out=None, mult=4, glu=False, dropout=0.):\n        super().__init__()\n        inner_dim = int(dim * mult)\n        dim_out = default(dim_out, dim)\n        project_in = nn.Sequential(\n            nn.Linear(dim, inner_dim),\n            nn.GELU()\n        ) if not glu else GEGLU(dim, inner_dim)\n\n        self.net = nn.Sequential(\n            project_in,\n            nn.Dropout(dropout),\n            nn.Linear(inner_dim, dim_out)\n        )\n\n    def forward(self, x):\n        return self.net(x)\n\n\ndef zero_module(module):\n    \"\"\"\n    Zero out the parameters of a module and return it.\n    \"\"\"\n    for p in module.parameters():\n        p.detach().zero_()\n    return module\n\n\ndef Normalize(in_channels):\n    return torch.nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True)\n\n\nclass LinearAttention(nn.Module):\n    def __init__(self, dim, heads=4, dim_head=32):\n        super().__init__()\n        self.heads = heads\n        hidden_dim = dim_head * heads\n        self.to_qkv = nn.Conv2d(dim, hidden_dim * 3, 1, bias = False)\n        self.to_out = nn.Conv2d(hidden_dim, dim, 1)\n\n    def forward(self, x):\n        b, c, h, w = x.shape\n        qkv = self.to_qkv(x)\n        q, k, v = rearrange(qkv, 'b (qkv heads c) h w -> qkv b heads c (h w)', heads = self.heads, qkv=3)\n        k = k.softmax(dim=-1)  \n        context = torch.einsum('bhdn,bhen->bhde', k, v)\n        out = torch.einsum('bhde,bhdn->bhen', context, q)\n        out = rearrange(out, 'b heads c (h w) -> b (heads c) h w', heads=self.heads, h=h, w=w)\n        return self.to_out(out)\n\n\nclass SpatialSelfAttention(nn.Module):\n    def __init__(self, in_channels):\n        super().__init__()\n        self.in_channels = in_channels\n\n        self.norm = Normalize(in_channels)\n        self.q = torch.nn.Conv2d(in_channels,\n                                 in_channels,\n                                 kernel_size=1,\n                                 stride=1,\n                                 padding=0)\n        self.k = torch.nn.Conv2d(in_channels,\n                                 in_channels,\n                                 kernel_size=1,\n                                 stride=1,\n                                 padding=0)\n        self.v = torch.nn.Conv2d(in_channels,\n                                 in_channels,\n                                 kernel_size=1,\n                                 stride=1,\n                                 padding=0)\n        self.proj_out = torch.nn.Conv2d(in_channels,\n                                        in_channels,\n                                        kernel_size=1,\n                                        stride=1,\n                                        padding=0)\n\n    def forward(self, x):\n        h_ = x\n        h_ = self.norm(h_)\n        q = self.q(h_)\n        k = self.k(h_)\n        v = self.v(h_)\n\n        # compute attention\n        b,c,h,w = q.shape\n        q = rearrange(q, 'b c h w -> b (h w) c')\n        k = rearrange(k, 'b c h w -> b c (h w)')\n        w_ = torch.einsum('bij,bjk->bik', q, k)\n\n        w_ = w_ * (int(c)**(-0.5))\n        w_ = torch.nn.functional.softmax(w_, dim=2)\n\n        # attend to values\n        v = rearrange(v, 'b c h w -> b c (h w)')\n        w_ = rearrange(w_, 'b i j -> b j i')\n        h_ = torch.einsum('bij,bjk->bik', v, w_)\n        h_ = rearrange(h_, 'b c (h w) -> b c h w', h=h)\n        h_ = self.proj_out(h_)\n\n        return x+h_\n\n\nclass CrossAttention(nn.Module):\n    def __init__(self, query_dim, context_dim=None, heads=8, dim_head=64, dropout=0.):\n        super().__init__()\n        inner_dim = dim_head * heads\n        context_dim = default(context_dim, query_dim)\n\n        self.scale = dim_head ** -0.5\n        self.heads = heads\n\n        self.to_q = nn.Linear(query_dim, inner_dim, bias=False)\n        self.to_k = nn.Linear(context_dim, inner_dim, bias=False)\n        self.to_v = nn.Linear(context_dim, inner_dim, bias=False)\n\n        self.to_out = nn.Sequential(\n            nn.Linear(inner_dim, query_dim),\n            nn.Dropout(dropout)\n        )\n\n    def forward(self, x, context=None, mask=None):\n        h = self.heads\n\n        q = self.to_q(x)\n        context = default(context, x)\n        k = self.to_k(context)\n        v = self.to_v(context)\n\n        q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> (b h) n d', h=h), (q, k, v))\n\n        sim = einsum('b i d, b j d -> b i j', q, k) * self.scale\n\n        if exists(mask):\n            mask = rearrange(mask, 'b ... -> b (...)')\n            max_neg_value = -torch.finfo(sim.dtype).max\n            mask = repeat(mask, 'b j -> (b h) () j', h=h)\n            sim.masked_fill_(~mask, max_neg_value)\n\n        # attention, what we cannot get enough of\n        attn = sim.softmax(dim=-1)\n\n        out = einsum('b i j, b j d -> b i d', attn, v)\n        out = rearrange(out, '(b h) n d -> b n (h d)', h=h)\n        return self.to_out(out)\n\nclass BasicTransformerBlock(nn.Module):\n    def __init__(self, dim, n_heads, d_head, dropout=0., context_dim=None, gated_ff=True):\n        super().__init__()\n        self.attn1 = CrossAttention(query_dim=dim, heads=n_heads, dim_head=d_head, dropout=dropout)  # is a self-attention\n        self.ff = FeedForward(dim, dropout=dropout, glu=gated_ff)\n        self.attn2 = CrossAttention(query_dim=dim, context_dim=context_dim,\n                                    heads=n_heads, dim_head=d_head, dropout=dropout)  # is self-attn if context is none\n        self.norm1 = nn.LayerNorm(dim)\n        self.norm2 = nn.LayerNorm(dim)\n        self.norm3 = nn.LayerNorm(dim)\n\n    def forward(self, x, context=None):\n        x = self.attn1(self.norm1(x)) + x\n        x = self.attn2(self.norm2(x), context=context) + x\n        x = self.ff(self.norm3(x)) + x\n        return x\n\n\ndef _is_power_of_2(n):\n    if (not isinstance(n, int)) or (n < 0):\n        raise ValueError(\"invalid input for _is_power_of_2: {} (type: {})\".format(n, type(n)))\n    return (n & (n-1) == 0) and n != 0\n\nclass DeformAttn(nn.Module):\n    def __init__(self, \n                 d_model=256,\n                 nheads=8,\n                 npoints=4,\n                 nlevels=4,\n                 key_dim=None):\n        super().__init__()\n        query_dim = d_model\n        key_dim = d_model\n        head_dim = d_model // nheads\n        \n        if d_model % nheads != 0:\n            raise ValueError('d_model must be divisible by n_heads, but got {} and {}'.format(d_model, nheads))\n        # you'd better set _d_per_head to a power of 2 which is more efficient in our CUDA implementation\n        if not _is_power_of_2(head_dim):\n            warnings.warn(\"You'd better set d_model in MSDeformAttn to make the dimension of each attention head a power of 2 \"\n                          \"which is more efficient in our CUDA implementation.\")\n        self.im2col_step = 64\n        \n        self.d_model = nheads * head_dim\n        key_dim = default(key_dim, query_dim)\n        \n        self.value_proj = nn.Linear(key_dim, nheads * head_dim)\n        self.sampling_offsets = nn.Linear(query_dim, nheads * nlevels * npoints * 2)\n        \n        self.attention_weights = nn.Linear(query_dim, nheads * nlevels * npoints)\n        self.output_proj = nn.Linear(nheads * head_dim, query_dim)\n        \n        self.n_heads = nheads\n        self.n_levels = nlevels\n        self.head_dim = head_dim\n        self.n_points = npoints\n        self._reset_parameters()\n    \n    def _reset_parameters(self):\n        constant_(self.sampling_offsets.weight.data, 0.)\n        thetas = torch.arange(self.n_heads, dtype=torch.float32) * (2.0 * math.pi / self.n_heads)\n        grid_init = torch.stack([thetas.cos(), thetas.sin()], -1)\n        grid_init = (grid_init / grid_init.abs().max(-1, keepdim=True)[0]).view(self.n_heads, 1, 1, 2).repeat(1, self.n_levels, self.n_points, 1)\n        for i in range(self.n_points):\n            grid_init[:, :, i, :] *= i + 1\n        with torch.no_grad():\n            self.sampling_offsets.bias = nn.Parameter(grid_init.view(-1))\n        constant_(self.attention_weights.weight.data, 0.)\n        constant_(self.attention_weights.bias.data, 0.)\n        xavier_uniform_(self.value_proj.weight.data)\n        constant_(self.value_proj.bias.data, 0.)\n        xavier_uniform_(self.output_proj.weight.data)\n        constant_(self.output_proj.bias.data, 0.)        \n\n    def forward(self, query, reference_points, \n                input_flatten, input_spatial_shapes, input_level_start_index,input_padding_mask=None):\n        \"\"\"\n        multi-scale deformable attention, self attention if query == input_flatten\n        Input:  \n            - query: \n                T(b n c)\n            - reference_points: center or reference boxes, normalized, [0, 1],  including padding area,\n                                         add additional (w, h) to form reference boxes\n                T(b n level 2) or T(b n level 4)\n            - input_flatten: multi-scale特征\n                T(b (h_\\sigma w_\\sigma) c)\n            - input_spatial_shapes: 每个level的大小\n                T(level 2)\n            - input_level_start_index: [0, level1_start, level2_start]\n            - input_padding_mask: True/False\n                T(b), (h_\\sigma w_\\sigma))\n        Output: \n            - query results:\n                T(b, n c)\n            - sampling_locations: normalized\n                T(b, n, m*l*k, 2)\n            - attention_weights: after softmax\n                T(b, n, m*l*k)\n        \"\"\"\n        batch_size, Nq, _ = query.shape\n        _, Nk, _ = input_flatten.shape\n        assert Nk == (input_spatial_shapes[:, 0] * input_spatial_shapes[:, 1]).sum()\n        \n        # B (h w) M * V\n        value = self.value_proj(input_flatten)\n        if input_padding_mask is not None:\n            value = value.masked_fill(input_padding_mask[...,None], float(0))\n        value = value.view(batch_size, Nk, self.n_heads, self.head_dim)\n        sampling_offesets = self.sampling_offsets(query).view(batch_size, Nq, self.n_heads,  self.n_levels, self.n_points, 2)\n        attention_weights= self.attention_weights(query).view(batch_size, Nq, self.n_heads, self.n_levels * self.n_points)\n        attention_weights = F.softmax(attention_weights, dim=-1).view(batch_size, Nq, self.n_heads, self.n_levels, self.n_points)\n        # b, n ,head, level, point, 2\n        if reference_points.shape[-1] == 2:\n            # T(2 level)\n            offset_normalizer = torch.stack([input_spatial_shapes[..., 1], input_spatial_shapes[..., 0]], -1)\n            sampling_locations = reference_points[:, :, None, :, None, :] + \\\n                sampling_offesets / offset_normalizer[None, None, None, :, None,:]\n        elif reference_points.shape[-1] == 4:\n            sampling_locations = reference_points[:, :, None, :, None, :2] + \\\n                sampling_offesets / self.n_points * reference_points[:, :, None, :, None, 2:] * 0.5\n        else:\n            raise NotImplementedError\n        output = MSDeformAttnFunction.apply(value, input_spatial_shapes, input_level_start_index, sampling_locations, attention_weights, self.im2col_step)\n        output = self.output_proj(output)\n        \n        return output, sampling_locations, attention_weights\n            \n        \nclass ContextuallSelfAttention(nn.Module):\n    def __init__(self, \n                 d_model, \n                 n_points,\n                 n_heads,\n                 context_dim=None):\n        super().__init__()\n        context_dim = default(context_dim, d_model)\n        query_dim = key_dim = d_model\n        \n        if d_model % n_heads != 0:\n            raise ValueError('d_model must be divisible by n_heads, but got {} and {}'.format(d_model, n_heads))\n        head_dim = d_model // n_heads\n        # you'd better set _d_per_head to a power of 2 which is more efficient in our CUDA implementation\n        if not _is_power_of_2(head_dim):\n            warnings.warn(\"You'd better set d_model in MSDeformAttn to make the dimension of each attention head a power of 2 \"\n                          \"which is more efficient in our CUDA implementation.\")\n        self.im2col_step = 64\n        \n        self.d_model = d_model\n        self.nheads = n_heads\n        self.head_dim = head_dim\n        self.npoints = n_points\n        \n        self.nlevels = 1\n        \n        self.value_proj = nn.Linear(key_dim, n_heads * head_dim)\n        self.sampling_offsets = nn.Linear(query_dim, n_heads * n_points * 2)\n        self.attention_weights = nn.Linear(query_dim, n_heads * n_points)\n        \n        self.output_proj = nn.Linear(n_heads * head_dim, query_dim)\n        \n    def forward(self,\n                context, context_mask,\n                query, reference_points, query_padding_mask = None,):\n        \"\"\"\n        contextual deformable attention\n        Input:  \n            - context:\n                T(b n c)\n            - context_mask:\n                T(b n)\n            - query: \n                T(b (h w) c)\n            - reference_points: center or reference boxes, normalized, [0, 1],  including padding area,\n                T(b (h w) 2/4)\n            - query_padding_mask:\n                T(b (h w))\n        Output: \n            - query results:\n                T(b, n c)\n            - sampling_locations: normalized\n                T(b, n, m*l*k, 2)\n            - attention_weights: after softmax\n                T(b, n, m*l*k)\n        \"\"\"\n        key = query\n        key_padding_mask = query_padding_mask\n        batch_size, Nq, _ = query.shape\n        Nk = Nq\n        \n        input_spatial_shapes = torch.tensor(query.shape[-2:]).unsqueeze(0)  # T(1, 2)\n        input_level_start_index = [0, ]\n        \n        # B (h w) M * V\n        value = self.value_proj(key)\n        if key_padding_mask is not None:\n            value = value.masked_fill(key_padding_mask[..., None], float(0))\n        value = value.view(batch_size, Nk, self.nheads, self.head_dim)\n        \n        sampling_offesets = self.sampling_offsets(query).view(batch_size, Nq, self.n_heads,  self.n_levels, self.n_points, 2)\n        attention_weights= self.attention_weights(query).view(batch_size, Nq, self.n_heads, self.n_levels * self.n_points)\n        attention_weights = F.softmax(attention_weights, dim=-1).view(batch_size, Nq, self.n_heads, self.n_levels, self.n_points)\n        # b, n ,head, level, point, 2\n        if reference_points.shape[-1] == 2:\n            # T(2 level)\n            offset_normalizer = torch.stack([input_spatial_shapes[..., 1], input_spatial_shapes[..., 0]], -1)\n            sampling_locations = reference_points[:, :, None, :, None, :] + \\\n                sampling_offesets / offset_normalizer[None, None, None, :, None,:]\n        elif reference_points.shape[-1] == 4:\n            sampling_locations = reference_points[:, :, None, :, None, :2] + \\\n                sampling_offesets / self.n_points * reference_points[:, :, None, :, None, 2:] * 0.5\n        else:\n            raise NotImplementedError\n        output = MSDeformAttnFunction.apply(value, input_spatial_shapes, input_level_start_index, sampling_locations, attention_weights, self.im2col_step)\n        output = self.output_proj(output)\n        \n        return output, sampling_locations, attention_weights\n\nclass BasicTransformerBlock_v2(nn.Module):\n    def __init__(self, dim, n_heads, d_head, dropout=0., context_dim=None, gated_ff=True):\n        super().__init__()\n        self.ff = FeedForward(dim, dropout=dropout, glu=gated_ff)\n        \n        self.norm1 = nn.LayerNorm(dim)\n        self.norm2 = nn.LayerNorm(dim)\n\n    def forward(self, x, context=None):\n        x = self.norm1(self.attn1(x, context=context) + x)\n        x = self.norm2(self.ff(x) + x)\n        return x\n\n\n\nclass SpatialTransformer(nn.Module):\n    \"\"\"\n    Transformer block for image-like data.\n    First, project the input (aka embedding)\n    and reshape to b, t, d.\n    Then apply standard transformer action.\n    Finally, reshape to image\n    \"\"\"\n    def __init__(self, in_channels, n_heads, d_head,\n                 depth=1, dropout=0., context_dim=None):\n        super().__init__()\n        self.in_channels = in_channels\n        inner_dim = n_heads * d_head\n        self.norm = Normalize(in_channels)\n\n        self.proj_in = nn.Conv2d(in_channels,\n                                 inner_dim,\n                                 kernel_size=1,\n                                 stride=1,\n                                 padding=0)\n\n        self.transformer_blocks = nn.ModuleList(\n            [BasicTransformerBlock(inner_dim, n_heads, d_head, dropout=dropout, context_dim=context_dim)\n                for d in range(depth)]\n        )\n\n        self.proj_out = zero_module(nn.Conv2d(inner_dim,\n                                              in_channels,\n                                              kernel_size=1,\n                                              stride=1,\n                                              padding=0))\n\n    def forward(self, x, context=None):\n        # note: if no context is given, cross-attention defaults to self-attention\n        b, c, h, w = x.shape\n        x_in = x\n        x = self.norm(x)\n        x = self.proj_in(x)\n        x = rearrange(x, 'b c h w -> b (h w) c')\n        for block in self.transformer_blocks:\n            x = block(x, context=context)\n        x = rearrange(x, 'b (h w) c -> b c h w', h=h, w=w)\n        x = self.proj_out(x)\n        return x + x_in\n    "
  },
  {
    "path": "models/encoder/ops/build/lib.linux-x86_64-cpython-311/functions/__init__.py",
    "content": "# ------------------------------------------------------------------------------------------------\n# Deformable DETR\n# Copyright (c) 2020 SenseTime. All Rights Reserved.\n# Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n# ------------------------------------------------------------------------------------------------\n# Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n# ------------------------------------------------------------------------------------------------\n\nfrom .ms_deform_attn_func import MSDeformAttnFunction\n\n"
  },
  {
    "path": "models/encoder/ops/build/lib.linux-x86_64-cpython-311/functions/ms_deform_attn_func.py",
    "content": "# ------------------------------------------------------------------------------------------------\n# Deformable DETR\n# Copyright (c) 2020 SenseTime. All Rights Reserved.\n# Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n# ------------------------------------------------------------------------------------------------\n# Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n# ------------------------------------------------------------------------------------------------\n\nfrom __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import division\n\nimport torch\nimport torch.nn.functional as F\nfrom torch.autograd import Function\nfrom torch.autograd.function import once_differentiable\n\nimport MultiScaleDeformableAttention as MSDA\n\n\nclass MSDeformAttnFunction(Function):\n    @staticmethod\n    def forward(ctx, value, value_spatial_shapes, value_level_start_index, sampling_locations, attention_weights, im2col_step):\n        ctx.im2col_step = im2col_step\n        output = MSDA.ms_deform_attn_forward(\n            value, value_spatial_shapes, value_level_start_index, sampling_locations, attention_weights, ctx.im2col_step)\n        ctx.save_for_backward(value, value_spatial_shapes, value_level_start_index, sampling_locations, attention_weights)\n        return output\n\n    @staticmethod\n    @once_differentiable\n    def backward(ctx, grad_output):\n        value, value_spatial_shapes, value_level_start_index, sampling_locations, attention_weights = ctx.saved_tensors\n        grad_value, grad_sampling_loc, grad_attn_weight = \\\n            MSDA.ms_deform_attn_backward(\n                value, value_spatial_shapes, value_level_start_index, sampling_locations, attention_weights, grad_output, ctx.im2col_step)\n\n        return grad_value, None, None, grad_sampling_loc, grad_attn_weight, None\n\n\ndef ms_deform_attn_core_pytorch(value, value_spatial_shapes, sampling_locations, attention_weights):\n    # for debug and test only,\n    # need to use cuda version instead\n    N_, S_, M_, D_ = value.shape\n    _, Lq_, M_, L_, P_, _ = sampling_locations.shape\n    value_list = value.split([H_ * W_ for H_, W_ in value_spatial_shapes], dim=1)\n    sampling_grids = 2 * sampling_locations - 1\n    sampling_value_list = []\n    for lid_, (H_, W_) in enumerate(value_spatial_shapes):\n        # N_, H_*W_, M_, D_ -> N_, H_*W_, M_*D_ -> N_, M_*D_, H_*W_ -> N_*M_, D_, H_, W_\n        value_l_ = value_list[lid_].flatten(2).transpose(1, 2).reshape(N_*M_, D_, H_, W_)\n        # N_, Lq_, M_, P_, 2 -> N_, M_, Lq_, P_, 2 -> N_*M_, Lq_, P_, 2\n        sampling_grid_l_ = sampling_grids[:, :, :, lid_].transpose(1, 2).flatten(0, 1)\n        # N_*M_, D_, Lq_, P_\n        sampling_value_l_ = F.grid_sample(value_l_, sampling_grid_l_,\n                                          mode='bilinear', padding_mode='zeros', align_corners=False)\n        sampling_value_list.append(sampling_value_l_)\n    # (N_, Lq_, M_, L_, P_) -> (N_, M_, Lq_, L_, P_) -> (N_, M_, 1, Lq_, L_*P_)\n    attention_weights = attention_weights.transpose(1, 2).reshape(N_*M_, 1, Lq_, L_*P_)\n    output = (torch.stack(sampling_value_list, dim=-2).flatten(-2) * attention_weights).sum(-1).view(N_, M_*D_, Lq_)\n    return output.transpose(1, 2).contiguous()\n"
  },
  {
    "path": "models/encoder/ops/build/lib.linux-x86_64-cpython-311/modules/__init__.py",
    "content": "# ------------------------------------------------------------------------------------------------\n# Deformable DETR\n# Copyright (c) 2020 SenseTime. All Rights Reserved.\n# Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n# ------------------------------------------------------------------------------------------------\n# Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n# ------------------------------------------------------------------------------------------------\n\nfrom .ms_deform_attn import MSDeformAttn\n"
  },
  {
    "path": "models/encoder/ops/build/lib.linux-x86_64-cpython-311/modules/ms_deform_attn.py",
    "content": "# Modify for sample points visualization\n# ------------------------------------------------------------------------------------------------\n# Deformable DETR\n# Copyright (c) 2020 SenseTime. All Rights Reserved.\n# Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n# ------------------------------------------------------------------------------------------------\n# Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n# ------------------------------------------------------------------------------------------------\n\nfrom __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import division\n\nimport warnings\nimport math\n\nimport torch\nfrom torch import nn\nimport torch.nn.functional as F\nfrom torch.nn.init import xavier_uniform_, constant_\n\nfrom ..functions import MSDeformAttnFunction\n\n\ndef _is_power_of_2(n):\n    if (not isinstance(n, int)) or (n < 0):\n        raise ValueError(\"invalid input for _is_power_of_2: {} (type: {})\".format(n, type(n)))\n    return (n & (n-1) == 0) and n != 0\n\n\nclass MSDeformAttn(nn.Module):\n    def __init__(self, d_model=256, n_levels=4, n_heads=8, n_points=4):\n        \"\"\"\n        Multi-Scale Deformable Attention Module\n        :param d_model      hidden dimension\n        :param n_levels     number of feature levels\n        :param n_heads      number of attention heads\n        :param n_points     number of sampling points per attention head per feature level\n        \"\"\"\n        super().__init__()\n        if d_model % n_heads != 0:\n            raise ValueError('d_model must be divisible by n_heads, but got {} and {}'.format(d_model, n_heads))\n        _d_per_head = d_model // n_heads\n        # you'd better set _d_per_head to a power of 2 which is more efficient in our CUDA implementation\n        if not _is_power_of_2(_d_per_head):\n            warnings.warn(\"You'd better set d_model in MSDeformAttn to make the dimension of each attention head a power of 2 \"\n                          \"which is more efficient in our CUDA implementation.\")\n\n        self.im2col_step = 64\n\n        self.d_model = d_model\n        self.n_levels = n_levels\n        self.n_heads = n_heads\n        self.n_points = n_points\n\n        self.sampling_offsets = nn.Linear(d_model, n_heads * n_levels * n_points * 2)\n        self.attention_weights = nn.Linear(d_model, n_heads * n_levels * n_points)\n        self.value_proj = nn.Linear(d_model, d_model)\n        self.output_proj = nn.Linear(d_model, d_model)\n\n        self._reset_parameters()\n\n    def _reset_parameters(self):\n        constant_(self.sampling_offsets.weight.data, 0.)\n        thetas = torch.arange(self.n_heads, dtype=torch.float32) * (2.0 * math.pi / self.n_heads)\n        grid_init = torch.stack([thetas.cos(), thetas.sin()], -1)\n        grid_init = (grid_init / grid_init.abs().max(-1, keepdim=True)[0]).view(self.n_heads, 1, 1, 2).repeat(1, self.n_levels, self.n_points, 1)\n        for i in range(self.n_points):\n            grid_init[:, :, i, :] *= i + 1\n        with torch.no_grad():\n            self.sampling_offsets.bias = nn.Parameter(grid_init.view(-1))\n        constant_(self.attention_weights.weight.data, 0.)\n        constant_(self.attention_weights.bias.data, 0.)\n        xavier_uniform_(self.value_proj.weight.data)\n        constant_(self.value_proj.bias.data, 0.)\n        xavier_uniform_(self.output_proj.weight.data)\n        constant_(self.output_proj.bias.data, 0.)\n\n    def forward(self, query, reference_points, input_flatten, input_spatial_shapes, input_level_start_index, input_padding_mask=None):\n        \"\"\"\n        :param query                       (N, Length_{query}, C)\n        :param reference_points            (N, Length_{query}, n_levels, 2), range in [0, 1], top-left (0,0), bottom-right (1, 1), including padding area\n                                        or (N, Length_{query}, n_levels, 4), add additional (w, h) to form reference boxes\n        :param input_flatten               (N, \\sum_{l=0}^{L-1} H_l \\cdot W_l, C)\n        :param input_spatial_shapes        (n_levels, 2), [(H_0, W_0), (H_1, W_1), ..., (H_{L-1}, W_{L-1})]\n        :param input_level_start_index     (n_levels, ), [0, H_0*W_0, H_0*W_0+H_1*W_1, H_0*W_0+H_1*W_1+H_2*W_2, ..., H_0*W_0+H_1*W_1+...+H_{L-1}*W_{L-1}]\n        :param input_padding_mask          (N, \\sum_{l=0}^{L-1} H_l \\cdot W_l), True for padding elements, False for non-padding elements\n\n        :return output                     (N, Length_{query}, C)\n        \"\"\"\n        N, Len_q, _ = query.shape\n        N, Len_in, _ = input_flatten.shape\n        assert (input_spatial_shapes[:, 0] * input_spatial_shapes[:, 1]).sum() == Len_in\n\n        value = self.value_proj(input_flatten)\n        if input_padding_mask is not None:\n            value = value.masked_fill(input_padding_mask[..., None], float(0))\n        value = value.view(N, Len_in, self.n_heads, self.d_model // self.n_heads)\n        sampling_offsets = self.sampling_offsets(query).view(N, Len_q, self.n_heads, self.n_levels, self.n_points, 2)\n        attention_weights = self.attention_weights(query).view(N, Len_q, self.n_heads, self.n_levels * self.n_points)\n        attention_weights = F.softmax(attention_weights, -1).view(N, Len_q, self.n_heads, self.n_levels, self.n_points)\n        # N, Len_q, n_heads, n_levels, n_points, 2\n        if reference_points.shape[-1] == 2:\n            offset_normalizer = torch.stack([input_spatial_shapes[..., 1], input_spatial_shapes[..., 0]], -1)\n            sampling_locations = reference_points[:, :, None, :, None, :] \\\n                                 + sampling_offsets / offset_normalizer[None, None, None, :, None, :]\n        elif reference_points.shape[-1] == 4:\n            sampling_locations = reference_points[:, :, None, :, None, :2] \\\n                                 + sampling_offsets / self.n_points * reference_points[:, :, None, :, None, 2:] * 0.5\n        else:\n            raise ValueError(\n                'Last dim of reference_points must be 2 or 4, but get {} instead.'.format(reference_points.shape[-1]))\n        output = MSDeformAttnFunction.apply(\n            value, input_spatial_shapes, input_level_start_index, sampling_locations, attention_weights, self.im2col_step)\n        output = self.output_proj(output)\n\n        return output, sampling_locations, attention_weights\n"
  },
  {
    "path": "models/encoder/ops/build/lib.linux-x86_64-cpython-38/functions/__init__.py",
    "content": "# ------------------------------------------------------------------------------------------------\n# Deformable DETR\n# Copyright (c) 2020 SenseTime. All Rights Reserved.\n# Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n# ------------------------------------------------------------------------------------------------\n# Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n# ------------------------------------------------------------------------------------------------\n\nfrom .ms_deform_attn_func import MSDeformAttnFunction\n\n"
  },
  {
    "path": "models/encoder/ops/build/lib.linux-x86_64-cpython-38/functions/ms_deform_attn_func.py",
    "content": "# ------------------------------------------------------------------------------------------------\n# Deformable DETR\n# Copyright (c) 2020 SenseTime. All Rights Reserved.\n# Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n# ------------------------------------------------------------------------------------------------\n# Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n# ------------------------------------------------------------------------------------------------\n\nfrom __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import division\n\nimport torch\nimport torch.nn.functional as F\nfrom torch.autograd import Function\nfrom torch.autograd.function import once_differentiable\n\nimport MultiScaleDeformableAttention as MSDA\n\n\nclass MSDeformAttnFunction(Function):\n    @staticmethod\n    def forward(ctx, value, value_spatial_shapes, value_level_start_index, sampling_locations, attention_weights, im2col_step):\n        ctx.im2col_step = im2col_step\n        output = MSDA.ms_deform_attn_forward(\n            value, value_spatial_shapes, value_level_start_index, sampling_locations, attention_weights, ctx.im2col_step)\n        ctx.save_for_backward(value, value_spatial_shapes, value_level_start_index, sampling_locations, attention_weights)\n        return output\n\n    @staticmethod\n    @once_differentiable\n    def backward(ctx, grad_output):\n        value, value_spatial_shapes, value_level_start_index, sampling_locations, attention_weights = ctx.saved_tensors\n        grad_value, grad_sampling_loc, grad_attn_weight = \\\n            MSDA.ms_deform_attn_backward(\n                value, value_spatial_shapes, value_level_start_index, sampling_locations, attention_weights, grad_output, ctx.im2col_step)\n\n        return grad_value, None, None, grad_sampling_loc, grad_attn_weight, None\n\n\ndef ms_deform_attn_core_pytorch(value, value_spatial_shapes, sampling_locations, attention_weights):\n    # for debug and test only,\n    # need to use cuda version instead\n    N_, S_, M_, D_ = value.shape\n    _, Lq_, M_, L_, P_, _ = sampling_locations.shape\n    value_list = value.split([H_ * W_ for H_, W_ in value_spatial_shapes], dim=1)\n    sampling_grids = 2 * sampling_locations - 1\n    sampling_value_list = []\n    for lid_, (H_, W_) in enumerate(value_spatial_shapes):\n        # N_, H_*W_, M_, D_ -> N_, H_*W_, M_*D_ -> N_, M_*D_, H_*W_ -> N_*M_, D_, H_, W_\n        value_l_ = value_list[lid_].flatten(2).transpose(1, 2).reshape(N_*M_, D_, H_, W_)\n        # N_, Lq_, M_, P_, 2 -> N_, M_, Lq_, P_, 2 -> N_*M_, Lq_, P_, 2\n        sampling_grid_l_ = sampling_grids[:, :, :, lid_].transpose(1, 2).flatten(0, 1)\n        # N_*M_, D_, Lq_, P_\n        sampling_value_l_ = F.grid_sample(value_l_, sampling_grid_l_,\n                                          mode='bilinear', padding_mode='zeros', align_corners=False)\n        sampling_value_list.append(sampling_value_l_)\n    # (N_, Lq_, M_, L_, P_) -> (N_, M_, Lq_, L_, P_) -> (N_, M_, 1, Lq_, L_*P_)\n    attention_weights = attention_weights.transpose(1, 2).reshape(N_*M_, 1, Lq_, L_*P_)\n    output = (torch.stack(sampling_value_list, dim=-2).flatten(-2) * attention_weights).sum(-1).view(N_, M_*D_, Lq_)\n    return output.transpose(1, 2).contiguous()\n"
  },
  {
    "path": "models/encoder/ops/build/lib.linux-x86_64-cpython-38/modules/__init__.py",
    "content": "# ------------------------------------------------------------------------------------------------\n# Deformable DETR\n# Copyright (c) 2020 SenseTime. All Rights Reserved.\n# Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n# ------------------------------------------------------------------------------------------------\n# Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n# ------------------------------------------------------------------------------------------------\n\nfrom .ms_deform_attn import MSDeformAttn\n"
  },
  {
    "path": "models/encoder/ops/build/lib.linux-x86_64-cpython-38/modules/ms_deform_attn.py",
    "content": "# Modify for sample points visualization\n# ------------------------------------------------------------------------------------------------\n# Deformable DETR\n# Copyright (c) 2020 SenseTime. All Rights Reserved.\n# Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n# ------------------------------------------------------------------------------------------------\n# Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n# ------------------------------------------------------------------------------------------------\n\nfrom __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import division\n\nimport warnings\nimport math\n\nimport torch\nfrom torch import nn\nimport torch.nn.functional as F\nfrom torch.nn.init import xavier_uniform_, constant_\n\nfrom ..functions import MSDeformAttnFunction\n\n\ndef _is_power_of_2(n):\n    if (not isinstance(n, int)) or (n < 0):\n        raise ValueError(\"invalid input for _is_power_of_2: {} (type: {})\".format(n, type(n)))\n    return (n & (n-1) == 0) and n != 0\n\n\nclass MSDeformAttn(nn.Module):\n    def __init__(self, d_model=256, n_levels=4, n_heads=8, n_points=4):\n        \"\"\"\n        Multi-Scale Deformable Attention Module\n        :param d_model      hidden dimension\n        :param n_levels     number of feature levels\n        :param n_heads      number of attention heads\n        :param n_points     number of sampling points per attention head per feature level\n        \"\"\"\n        super().__init__()\n        if d_model % n_heads != 0:\n            raise ValueError('d_model must be divisible by n_heads, but got {} and {}'.format(d_model, n_heads))\n        _d_per_head = d_model // n_heads\n        # you'd better set _d_per_head to a power of 2 which is more efficient in our CUDA implementation\n        if not _is_power_of_2(_d_per_head):\n            warnings.warn(\"You'd better set d_model in MSDeformAttn to make the dimension of each attention head a power of 2 \"\n                          \"which is more efficient in our CUDA implementation.\")\n\n        self.im2col_step = 64\n\n        self.d_model = d_model\n        self.n_levels = n_levels\n        self.n_heads = n_heads\n        self.n_points = n_points\n\n        self.sampling_offsets = nn.Linear(d_model, n_heads * n_levels * n_points * 2)\n        self.attention_weights = nn.Linear(d_model, n_heads * n_levels * n_points)\n        self.value_proj = nn.Linear(d_model, d_model)\n        self.output_proj = nn.Linear(d_model, d_model)\n\n        self._reset_parameters()\n\n    def _reset_parameters(self):\n        constant_(self.sampling_offsets.weight.data, 0.)\n        thetas = torch.arange(self.n_heads, dtype=torch.float32) * (2.0 * math.pi / self.n_heads)\n        grid_init = torch.stack([thetas.cos(), thetas.sin()], -1)\n        grid_init = (grid_init / grid_init.abs().max(-1, keepdim=True)[0]).view(self.n_heads, 1, 1, 2).repeat(1, self.n_levels, self.n_points, 1)\n        for i in range(self.n_points):\n            grid_init[:, :, i, :] *= i + 1\n        with torch.no_grad():\n            self.sampling_offsets.bias = nn.Parameter(grid_init.view(-1))\n        constant_(self.attention_weights.weight.data, 0.)\n        constant_(self.attention_weights.bias.data, 0.)\n        xavier_uniform_(self.value_proj.weight.data)\n        constant_(self.value_proj.bias.data, 0.)\n        xavier_uniform_(self.output_proj.weight.data)\n        constant_(self.output_proj.bias.data, 0.)\n\n    def forward(self, query, reference_points, input_flatten, input_spatial_shapes, input_level_start_index, input_padding_mask=None):\n        \"\"\"\n        :param query                       (N, Length_{query}, C)\n        :param reference_points            (N, Length_{query}, n_levels, 2), range in [0, 1], top-left (0,0), bottom-right (1, 1), including padding area\n                                        or (N, Length_{query}, n_levels, 4), add additional (w, h) to form reference boxes\n        :param input_flatten               (N, \\sum_{l=0}^{L-1} H_l \\cdot W_l, C)\n        :param input_spatial_shapes        (n_levels, 2), [(H_0, W_0), (H_1, W_1), ..., (H_{L-1}, W_{L-1})]\n        :param input_level_start_index     (n_levels, ), [0, H_0*W_0, H_0*W_0+H_1*W_1, H_0*W_0+H_1*W_1+H_2*W_2, ..., H_0*W_0+H_1*W_1+...+H_{L-1}*W_{L-1}]\n        :param input_padding_mask          (N, \\sum_{l=0}^{L-1} H_l \\cdot W_l), True for padding elements, False for non-padding elements\n\n        :return output                     (N, Length_{query}, C)\n        \"\"\"\n        N, Len_q, _ = query.shape\n        N, Len_in, _ = input_flatten.shape\n        assert (input_spatial_shapes[:, 0] * input_spatial_shapes[:, 1]).sum() == Len_in\n\n        value = self.value_proj(input_flatten)\n        if input_padding_mask is not None:\n            value = value.masked_fill(input_padding_mask[..., None], float(0))\n        value = value.view(N, Len_in, self.n_heads, self.d_model // self.n_heads)\n        sampling_offsets = self.sampling_offsets(query).view(N, Len_q, self.n_heads, self.n_levels, self.n_points, 2)\n        attention_weights = self.attention_weights(query).view(N, Len_q, self.n_heads, self.n_levels * self.n_points)\n        attention_weights = F.softmax(attention_weights, -1).view(N, Len_q, self.n_heads, self.n_levels, self.n_points)\n        # N, Len_q, n_heads, n_levels, n_points, 2\n        if reference_points.shape[-1] == 2:\n            offset_normalizer = torch.stack([input_spatial_shapes[..., 1], input_spatial_shapes[..., 0]], -1)\n            sampling_locations = reference_points[:, :, None, :, None, :] \\\n                                 + sampling_offsets / offset_normalizer[None, None, None, :, None, :]\n        elif reference_points.shape[-1] == 4:\n            sampling_locations = reference_points[:, :, None, :, None, :2] \\\n                                 + sampling_offsets / self.n_points * reference_points[:, :, None, :, None, 2:] * 0.5\n        else:\n            raise ValueError(\n                'Last dim of reference_points must be 2 or 4, but get {} instead.'.format(reference_points.shape[-1]))\n        output = MSDeformAttnFunction.apply(\n            value, input_spatial_shapes, input_level_start_index, sampling_locations, attention_weights, self.im2col_step)\n        output = self.output_proj(output)\n\n        return output, sampling_locations, attention_weights\n"
  },
  {
    "path": "models/encoder/ops/build/temp.linux-x86_64-cpython-311/.ninja_log",
    "content": "# ninja log v5\n0\t5344\t1685604027\t/home/xhh/workspace/rvos_encoder/models/ops/build/temp.linux-x86_64-cpython-311/home/xhh/workspace/rvos_encoder/models/ops/src/cpu/ms_deform_attn_cpu.o\t1eaabdd4515aceab\n1\t20910\t1685604042\t/home/xhh/workspace/rvos_encoder/models/ops/build/temp.linux-x86_64-cpython-311/home/xhh/workspace/rvos_encoder/models/ops/src/vision.o\tb8641c4a4f7766f9\n0\t21063\t1685604042\t/home/xhh/workspace/rvos_encoder/models/ops/build/temp.linux-x86_64-cpython-311/home/xhh/workspace/rvos_encoder/models/ops/src/cuda/ms_deform_attn_cuda.o\td77fcd8ae1c377bb\n"
  },
  {
    "path": "models/encoder/ops/build/temp.linux-x86_64-cpython-311/build.ninja",
    "content": "ninja_required_version = 1.3\ncxx = c++\nnvcc = /usr/local/cuda/bin/nvcc\n\ncflags = -pthread -B /home/xhh/anaconda3/envs/natten/compiler_compat -DNDEBUG -fwrapv -O2 -Wall -fPIC -O2 -isystem /home/xhh/anaconda3/envs/natten/include -fPIC -O2 -isystem /home/xhh/anaconda3/envs/natten/include -fPIC -DWITH_CUDA -I/home/xhh/workspace/rvos_encoder/models/ops/src -I/home/xhh/anaconda3/envs/natten/lib/python3.11/site-packages/torch/include -I/home/xhh/anaconda3/envs/natten/lib/python3.11/site-packages/torch/include/torch/csrc/api/include -I/home/xhh/anaconda3/envs/natten/lib/python3.11/site-packages/torch/include/TH -I/home/xhh/anaconda3/envs/natten/lib/python3.11/site-packages/torch/include/THC -I/usr/local/cuda/include -I/home/xhh/anaconda3/envs/natten/include/python3.11 -c\npost_cflags = -DTORCH_API_INCLUDE_EXTENSION_H '-DPYBIND11_COMPILER_TYPE=\"_gcc\"' '-DPYBIND11_STDLIB=\"_libstdcpp\"' '-DPYBIND11_BUILD_ABI=\"_cxxabi1011\"' -DTORCH_EXTENSION_NAME=MultiScaleDeformableAttention -D_GLIBCXX_USE_CXX11_ABI=0 -std=c++17\ncuda_cflags = -DWITH_CUDA -I/home/xhh/workspace/rvos_encoder/models/ops/src -I/home/xhh/anaconda3/envs/natten/lib/python3.11/site-packages/torch/include -I/home/xhh/anaconda3/envs/natten/lib/python3.11/site-packages/torch/include/torch/csrc/api/include -I/home/xhh/anaconda3/envs/natten/lib/python3.11/site-packages/torch/include/TH -I/home/xhh/anaconda3/envs/natten/lib/python3.11/site-packages/torch/include/THC -I/usr/local/cuda/include -I/home/xhh/anaconda3/envs/natten/include/python3.11 -c\ncuda_post_cflags = -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_BFLOAT16_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ --expt-relaxed-constexpr --compiler-options ''\"'\"'-fPIC'\"'\"'' -DCUDA_HAS_FP16=1 -D__CUDA_NO_HALF_OPERATORS__ -D__CUDA_NO_HALF_CONVERSIONS__ -D__CUDA_NO_HALF2_OPERATORS__ -DTORCH_API_INCLUDE_EXTENSION_H '-DPYBIND11_COMPILER_TYPE=\"_gcc\"' '-DPYBIND11_STDLIB=\"_libstdcpp\"' '-DPYBIND11_BUILD_ABI=\"_cxxabi1011\"' -DTORCH_EXTENSION_NAME=MultiScaleDeformableAttention -D_GLIBCXX_USE_CXX11_ABI=0 -gencode=arch=compute_86,code=compute_86 -gencode=arch=compute_86,code=sm_86 -std=c++17\ncuda_dlink_post_cflags = \nldflags = \n\nrule compile\n  command = $cxx -MMD -MF $out.d $cflags -c $in -o $out $post_cflags\n  depfile = $out.d\n  deps = gcc\n\nrule cuda_compile\n  depfile = $out.d\n  deps = gcc\n  command = $nvcc  $cuda_cflags -c $in -o $out $cuda_post_cflags\n\n\n\n\n\nbuild /home/xhh/workspace/rvos_encoder/models/ops/build/temp.linux-x86_64-cpython-311/home/xhh/workspace/rvos_encoder/models/ops/src/cpu/ms_deform_attn_cpu.o: compile /home/xhh/workspace/rvos_encoder/models/ops/src/cpu/ms_deform_attn_cpu.cpp\nbuild /home/xhh/workspace/rvos_encoder/models/ops/build/temp.linux-x86_64-cpython-311/home/xhh/workspace/rvos_encoder/models/ops/src/cuda/ms_deform_attn_cuda.o: cuda_compile /home/xhh/workspace/rvos_encoder/models/ops/src/cuda/ms_deform_attn_cuda.cu\nbuild /home/xhh/workspace/rvos_encoder/models/ops/build/temp.linux-x86_64-cpython-311/home/xhh/workspace/rvos_encoder/models/ops/src/vision.o: compile /home/xhh/workspace/rvos_encoder/models/ops/src/vision.cpp\n\n\n\n\n\n\n\n"
  },
  {
    "path": "models/encoder/ops/functions/__init__.py",
    "content": "# ------------------------------------------------------------------------------------------------\n# Deformable DETR\n# Copyright (c) 2020 SenseTime. All Rights Reserved.\n# Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n# ------------------------------------------------------------------------------------------------\n# Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n# ------------------------------------------------------------------------------------------------\n\nfrom .ms_deform_attn_func import MSDeformAttnFunction\n\n"
  },
  {
    "path": "models/encoder/ops/functions/ms_deform_attn_func.py",
    "content": "# ------------------------------------------------------------------------------------------------\n# Deformable DETR\n# Copyright (c) 2020 SenseTime. All Rights Reserved.\n# Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n# ------------------------------------------------------------------------------------------------\n# Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n# ------------------------------------------------------------------------------------------------\n\nfrom __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import division\n\nimport torch\nimport torch.nn.functional as F\nfrom torch.autograd import Function\nfrom torch.autograd.function import once_differentiable\n\nimport MultiScaleDeformableAttention as MSDA\n\n\nclass MSDeformAttnFunction(Function):\n    @staticmethod\n    def forward(ctx, value, value_spatial_shapes, value_level_start_index, sampling_locations, attention_weights, im2col_step):\n        ctx.im2col_step = im2col_step\n        output = MSDA.ms_deform_attn_forward(\n            value, value_spatial_shapes, value_level_start_index, sampling_locations, attention_weights, ctx.im2col_step)\n        ctx.save_for_backward(value, value_spatial_shapes, value_level_start_index, sampling_locations, attention_weights)\n        return output\n\n    @staticmethod\n    @once_differentiable\n    def backward(ctx, grad_output):\n        value, value_spatial_shapes, value_level_start_index, sampling_locations, attention_weights = ctx.saved_tensors\n        grad_value, grad_sampling_loc, grad_attn_weight = \\\n            MSDA.ms_deform_attn_backward(\n                value, value_spatial_shapes, value_level_start_index, sampling_locations, attention_weights, grad_output, ctx.im2col_step)\n\n        return grad_value, None, None, grad_sampling_loc, grad_attn_weight, None\n\n\ndef ms_deform_attn_core_pytorch(value, value_spatial_shapes, sampling_locations, attention_weights):\n    # for debug and test only,\n    # need to use cuda version instead\n    N_, S_, M_, D_ = value.shape\n    _, Lq_, M_, L_, P_, _ = sampling_locations.shape\n    value_list = value.split([H_ * W_ for H_, W_ in value_spatial_shapes], dim=1)\n    sampling_grids = 2 * sampling_locations - 1\n    sampling_value_list = []\n    for lid_, (H_, W_) in enumerate(value_spatial_shapes):\n        # N_, H_*W_, M_, D_ -> N_, H_*W_, M_*D_ -> N_, M_*D_, H_*W_ -> N_*M_, D_, H_, W_\n        value_l_ = value_list[lid_].flatten(2).transpose(1, 2).reshape(N_*M_, D_, H_, W_)\n        # N_, Lq_, M_, P_, 2 -> N_, M_, Lq_, P_, 2 -> N_*M_, Lq_, P_, 2\n        sampling_grid_l_ = sampling_grids[:, :, :, lid_].transpose(1, 2).flatten(0, 1)\n        # N_*M_, D_, Lq_, P_\n        sampling_value_l_ = F.grid_sample(value_l_, sampling_grid_l_,\n                                          mode='bilinear', padding_mode='zeros', align_corners=False)\n        sampling_value_list.append(sampling_value_l_)\n    # (N_, Lq_, M_, L_, P_) -> (N_, M_, Lq_, L_, P_) -> (N_, M_, 1, Lq_, L_*P_)\n    attention_weights = attention_weights.transpose(1, 2).reshape(N_*M_, 1, Lq_, L_*P_)\n    output = (torch.stack(sampling_value_list, dim=-2).flatten(-2) * attention_weights).sum(-1).view(N_, M_*D_, Lq_)\n    return output.transpose(1, 2).contiguous()\n"
  },
  {
    "path": "models/encoder/ops/make.sh",
    "content": "#!/usr/bin/env bash\n# ------------------------------------------------------------------------------------------------\n# Deformable DETR\n# Copyright (c) 2020 SenseTime. All Rights Reserved.\n# Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n# ------------------------------------------------------------------------------------------------\n# Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n# ------------------------------------------------------------------------------------------------\n\npython setup.py build install\n"
  },
  {
    "path": "models/encoder/ops/modules/__init__.py",
    "content": "# ------------------------------------------------------------------------------------------------\n# Deformable DETR\n# Copyright (c) 2020 SenseTime. All Rights Reserved.\n# Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n# ------------------------------------------------------------------------------------------------\n# Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n# ------------------------------------------------------------------------------------------------\n\nfrom .ms_deform_attn import MSDeformAttn\nfrom . import frame_query_ss2d\n"
  },
  {
    "path": "models/encoder/ops/modules/frame_query_ss2d.py",
    "content": "\nfrom models.layers.position_encoding import build_position_encoding\nfrom mamba_ssm.ops.selective_scan_interface import selective_scan_fn, selective_scan_ref\nimport math\nimport warnings\nimport math\n\nimport torch\nfrom torch import nn\nimport torch.nn.functional as F\nfrom torch.nn.init import xavier_uniform_, constant_\n\nfrom ..functions import MSDeformAttnFunction\n\nfrom mamba_ssm import Mamba\nfrom einops import rearrange, reduce, repeat\nfrom detectron2.modeling import META_ARCH_REGISTRY\n\n# v1\nclass SS2D(nn.Module):\n    def __init__(\n        self,\n        d_model,\n        d_state=16,\n        # d_state=\"auto\", # 20240109\n        d_conv=3,\n        expand=2,\n        dt_rank=\"auto\",\n        dt_min=0.001,\n        dt_max=0.1,\n        dt_init=\"random\",\n        dt_scale=1.0,\n        dt_init_floor=1e-4,\n        dropout=0.,\n        conv_bias=True,\n        bias=False,\n        device=None,\n        dtype=None,\n        **kwargs,\n    ):\n        factory_kwargs = {\"device\": device, \"dtype\": dtype}\n        super().__init__()\n        self.d_model = d_model\n        self.d_state = d_state\n        # self.d_state = math.ceil(self.d_model / 6) if d_state == \"auto\" else d_model # 20240109\n        self.d_conv = d_conv\n        self.expand = expand\n        self.d_inner = int(self.expand * self.d_model)\n        self.dt_rank = math.ceil(self.d_model / 16) if dt_rank == \"auto\" else dt_rank\n\n        self.in_proj = nn.Linear(self.d_model, self.d_inner * 2, bias=bias, **factory_kwargs)\n        self.conv2d = nn.Conv2d(\n            in_channels=self.d_inner,\n            out_channels=self.d_inner,\n            groups=self.d_inner,\n            bias=conv_bias,\n            kernel_size=d_conv,\n            padding=(d_conv - 1) // 2,\n            **factory_kwargs,\n        )\n        self.act = nn.SiLU()\n\n        self.x_proj = (\n            nn.Linear(self.d_inner, (self.dt_rank + self.d_state * 2), bias=False, **factory_kwargs), \n            nn.Linear(self.d_inner, (self.dt_rank + self.d_state * 2), bias=False, **factory_kwargs), \n            nn.Linear(self.d_inner, (self.dt_rank + self.d_state * 2), bias=False, **factory_kwargs), \n            nn.Linear(self.d_inner, (self.dt_rank + self.d_state * 2), bias=False, **factory_kwargs), \n        )\n        self.x_proj_weight = nn.Parameter(torch.stack([t.weight for t in self.x_proj], dim=0)) # (K=4, N, inner)\n        del self.x_proj\n\n        self.dt_projs = (\n            self.dt_init(self.dt_rank, self.d_inner, dt_scale, dt_init, dt_min, dt_max, dt_init_floor, **factory_kwargs),\n            self.dt_init(self.dt_rank, self.d_inner, dt_scale, dt_init, dt_min, dt_max, dt_init_floor, **factory_kwargs),\n            self.dt_init(self.dt_rank, self.d_inner, dt_scale, dt_init, dt_min, dt_max, dt_init_floor, **factory_kwargs),\n            self.dt_init(self.dt_rank, self.d_inner, dt_scale, dt_init, dt_min, dt_max, dt_init_floor, **factory_kwargs),\n        )\n        self.dt_projs_weight = nn.Parameter(torch.stack([t.weight for t in self.dt_projs], dim=0)) # (K=4, inner, rank)\n        self.dt_projs_bias = nn.Parameter(torch.stack([t.bias for t in self.dt_projs], dim=0)) # (K=4, inner)\n        del self.dt_projs\n        \n        self.A_logs = self.A_log_init(self.d_state, self.d_inner, copies=4, merge=True) # (K=4, D, N)\n        self.Ds = self.D_init(self.d_inner, copies=4, merge=True) # (K=4, D, N)\n\n        # self.selective_scan = selective_scan_fn\n        self.forward_core = self.forward_corev0\n\n        self.out_norm = nn.LayerNorm(self.d_inner)\n        self.out_proj = nn.Linear(self.d_inner, self.d_model, bias=bias, **factory_kwargs)\n        self.dropout = nn.Dropout(dropout) if dropout > 0. else None\n\n    @staticmethod\n    def dt_init(dt_rank, d_inner, dt_scale=1.0, dt_init=\"random\", dt_min=0.001, dt_max=0.1, dt_init_floor=1e-4, **factory_kwargs):\n        dt_proj = nn.Linear(dt_rank, d_inner, bias=True, **factory_kwargs)\n\n        # Initialize special dt projection to preserve variance at initialization\n        dt_init_std = dt_rank**-0.5 * dt_scale\n        if dt_init == \"constant\":\n            nn.init.constant_(dt_proj.weight, dt_init_std)\n        elif dt_init == \"random\":\n            nn.init.uniform_(dt_proj.weight, -dt_init_std, dt_init_std)\n        else:\n            raise NotImplementedError\n\n        # Initialize dt bias so that F.softplus(dt_bias) is between dt_min and dt_max\n        dt = torch.exp(\n            torch.rand(d_inner, **factory_kwargs) * (math.log(dt_max) - math.log(dt_min))\n            + math.log(dt_min)\n        ).clamp(min=dt_init_floor)\n        # Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759\n        inv_dt = dt + torch.log(-torch.expm1(-dt))\n        with torch.no_grad():\n            dt_proj.bias.copy_(inv_dt)\n        # Our initialization would set all Linear.bias to zero, need to mark this one as _no_reinit\n        dt_proj.bias._no_reinit = True\n        \n        return dt_proj\n\n    @staticmethod\n    def A_log_init(d_state, d_inner, copies=1, device=None, merge=True):\n        # S4D real initialization\n        A = repeat(\n            torch.arange(1, d_state + 1, dtype=torch.float32, device=device),\n            \"n -> d n\",\n            d=d_inner,\n        ).contiguous()\n        A_log = torch.log(A)  # Keep A_log in fp32\n        if copies > 1:\n            A_log = repeat(A_log, \"d n -> r d n\", r=copies)\n            if merge:\n                A_log = A_log.flatten(0, 1)\n        A_log = nn.Parameter(A_log)\n        A_log._no_weight_decay = True\n        return A_log\n\n    @staticmethod\n    def D_init(d_inner, copies=1, device=None, merge=True):\n        # D \"skip\" parameter\n        D = torch.ones(d_inner, device=device)\n        if copies > 1:\n            D = repeat(D, \"n1 -> r n1\", r=copies)\n            if merge:\n                D = D.flatten(0, 1)\n        D = nn.Parameter(D)  # Keep in fp32\n        D._no_weight_decay = True\n        return D\n\n    def forward_corev0(self, x: torch.Tensor):\n        self.selective_scan = selective_scan_fn\n        \n        B, C, H, W = x.shape\n        L = H * W\n        K = 4\n\n        x_hwwh = torch.stack([x.view(B, -1, L), torch.transpose(x, dim0=2, dim1=3).contiguous().view(B, -1, L)], dim=1).view(B, 2, -1, L)\n        xs = torch.cat([x_hwwh, torch.flip(x_hwwh, dims=[-1])], dim=1) # (b, k, d, l)\n\n        x_dbl = torch.einsum(\"b k d l, k c d -> b k c l\", xs.view(B, K, -1, L), self.x_proj_weight)\n        # x_dbl = x_dbl + self.x_proj_bias.view(1, K, -1, 1)\n        dts, Bs, Cs = torch.split(x_dbl, [self.dt_rank, self.d_state, self.d_state], dim=2)\n        dts = torch.einsum(\"b k r l, k d r -> b k d l\", dts.view(B, K, -1, L), self.dt_projs_weight)\n        # dts = dts + self.dt_projs_bias.view(1, K, -1, 1)\n\n        xs = xs.float().view(B, -1, L) # (b, k * d, l)\n        dts = dts.contiguous().float().view(B, -1, L) # (b, k * d, l)\n        Bs = Bs.float().view(B, K, -1, L) # (b, k, d_state, l)\n        Cs = Cs.float().view(B, K, -1, L) # (b, k, d_state, l)\n        Ds = self.Ds.float().view(-1) # (k * d)\n        As = -torch.exp(self.A_logs.float()).view(-1, self.d_state)  # (k * d, d_state)\n        dt_projs_bias = self.dt_projs_bias.float().view(-1) # (k * d)\n\n        out_y = self.selective_scan(\n            xs, dts, \n            As, Bs, Cs, Ds, z=None,\n            delta_bias=dt_projs_bias,\n            delta_softplus=True,\n            return_last_state=False,\n        ).view(B, K, -1, L)\n        assert out_y.dtype == torch.float\n\n        inv_y = torch.flip(out_y[:, 2:4], dims=[-1]).view(B, 2, -1, L)\n        wh_y = torch.transpose(out_y[:, 1].view(B, -1, W, H), dim0=2, dim1=3).contiguous().view(B, -1, L)\n        invwh_y = torch.transpose(inv_y[:, 1].view(B, -1, W, H), dim0=2, dim1=3).contiguous().view(B, -1, L)\n\n        return out_y[:, 0], inv_y[:, 0], wh_y, invwh_y\n\n    def forward(self, x: torch.Tensor, **kwargs):\n        B, H, W, C = x.shape\n        xz = self.in_proj(x)\n        x, z = xz.chunk(2, dim=-1) # (b, h, w, d)\n\n        x = x.permute(0, 3, 1, 2).contiguous()\n        x = self.act(self.conv2d(x)) # (b, d, h, w)\n        y1, y2, y3, y4 = self.forward_core(x) # B C hw\n        assert y1.dtype == torch.float32\n        y = y1 + y2 + y3 + y4\n        y = torch.transpose(y, dim0=1, dim1=2).contiguous().view(B, H, W, -1)\n        y = self.out_norm(y)\n        y = y * F.silu(z)\n        out = self.out_proj(y)\n        if self.dropout is not None:\n            out = self.dropout(out)\n        return out\n\nclass SS2D_FrameQuery(nn.Module):\n    def __init__(self, configs,):\n        super().__init__()\n        d_model = configs['d_model']\n        self.homo = SS2D(d_model=configs['d_model'],\n                        d_state=configs['d_state'] if 'd_state' in configs else 16,\n                        d_conv=configs['d_conv'] if 'd_conv' in configs else 3,\n                        expand=configs['expand'] if 'expand' in configs else 2,\n                        dt_rank=configs['dt_rank'] if 'dt_rank' in configs else 'auto',\n                        dt_min=configs['dt_min'] if 'dt_min' in configs else 0.001,\n                        dt_max=configs['dt_max'] if 'dt_max' in configs else 0.1,\n                        dt_init=configs['dt_init'] if 'dt_init' in configs else 'random',\n                        dt_scale=configs['dt_scale'] if 'dt_scale' in configs else 1.0,\n                        dt_init_floor=configs['dt_init_floor'] if 'dt_init_floor' in configs else 1e-4,\n                        dropout=configs['dropout'] if 'dropout' in configs else 0,\n                        conv_bias=configs['conv_bias'] if 'conv_bias' in configs else True,\n                        bias=configs['bias'] if 'bias' in configs else False,\n                        )\n        self.pos_1d = build_position_encoding(position_embedding_name='1d')  # t上的position embedding\n\n    def forward(self, \n                frame_query_feats=None,  # n bt c  \n                frame_query_poses=None, # n bt c  # nq上的Position embedding\n                nf=None,\n                **kwargs\n                ):\n        batch_size = frame_query_feats.shape[1] // nf # b\n        frame_query_feats += frame_query_poses  \n        frame_query_feats = rearrange(frame_query_feats, 'n (b t) c -> b t n c',b=batch_size,t=nf).contiguous()\n\n        sin_poses = self.pos_1d(torch.zeros_like(frame_query_feats[..., 0].permute(0, 2, 1).flatten(0, 1)).bool(), \n                                hidden_dim=frame_query_feats.shape[-1]) # bn c t\n        sin_poses = rearrange(sin_poses, '(b n) c t -> b t n c', b=batch_size)\n        frame_query_feats += sin_poses\n\n        frame_query_feats = self.homo(frame_query_feats) # b t n c\n        \n        frame_query_feats = frame_query_feats.permute(2, 0, 1, 3).flatten(1, 2).contiguous() # n bt c\n\n        return frame_query_feats, None\n\n@META_ARCH_REGISTRY.register()\nclass FrameQuery_SS2DLayer(nn.Module):\n    def __init__(self, \n                 configs, \n                 dropout=0.0,\n                 activation=\"relu\", normalize_before=False):\n        super().__init__()\n        d_model = configs['d_model']\n        dropout = configs['dropout']\n        self.self_attn = SS2D_FrameQuery(configs)\n        self.norm = nn.LayerNorm(d_model)\n        self.dropout = nn.Dropout(dropout)\n\n        from models.layers.decoder_layers import FFNLayer\n        self.ffn = FFNLayer(d_model=d_model,\n                            dim_feedforward=configs['dim_feedforward'],\n                            dropout=dropout,)\n\n    def forward(self, \n                frame_query_feats,  # n bt c  \n                frame_query_poses, # n bt c  # nq上的Position embedding\n                nf=None,\n                **kwargs):\n        \n        tgt2 = self.self_attn(frame_query_feats=frame_query_feats,  # n bt c  \n                             frame_query_poses=frame_query_poses, # n bt c  # nq上的Position embedding\n                             nf=nf,)[0]\n        frame_query_feats = frame_query_feats + self.dropout(tgt2)\n        frame_query_feats = self.norm(frame_query_feats)\n\n        frame_query_feats = self.ffn(frame_query_feats)\n        return frame_query_feats\n\nfrom models.layers.decoder_layers import CrossAttentionLayer, SelfAttentionLayer, FFNLayer\n@META_ARCH_REGISTRY.register()\nclass TemporalQuery_CrossSelf(nn.Module):\n    def __init__(self, configs) -> None:\n        super().__init__()\n        d_model = configs['d_model']\n        attn_configs = configs['attn']\n        self.cross_layers = CrossAttentionLayer(d_model=d_model,\n                                                nhead=attn_configs['nheads'],\n                                                dropout=0.0,\n                                                normalize_before=attn_configs['normalize_before'])\n        self.self_layers = SelfAttentionLayer(d_model=d_model,\n                                                nhead=attn_configs['nheads'],\n                                                dropout=0.0,\n                                                normalize_before=attn_configs['normalize_before'])\n        self.ffn_layers = FFNLayer(d_model=d_model,\n                                    dim_feedforward=attn_configs['dim_feedforward'],\n                                    dropout=0.0,\n                                    normalize_before=attn_configs['normalize_before'])\n    \n    def forward(self, \n                temporal_query_feats, \n                temporal_query_poses,\n                frame_query_feats, frame_query_poses,\n                video_aux_dict=None, **kwargs):\n        # nq b c; nq bt c\n        nq, batch_size, _ = temporal_query_feats.shape\n        nf = frame_query_feats.shape[1] // batch_size\n        nqf = frame_query_feats.shape[0]\n        frame_query_feats = rearrange(frame_query_feats, 'nq (b t) c -> (t nq) b c',b=batch_size, t=nf)\n        frame_query_poses = rearrange(frame_query_poses, 'nq (b t) c -> (t nq) b c',b=batch_size, t=nf)\n        temporal_query_feats = self.cross_layers(\n            tgt=temporal_query_feats, # n b c\n            memory=frame_query_feats,  # t nqf b c\n            pos=frame_query_poses, \n            query_pos=temporal_query_poses,\n        )\n        temporal_query_feats = self.self_layers(\n            temporal_query_feats,\n            query_pos=temporal_query_poses,\n        )\n        temporal_query_feats = self.ffn_layers(\n            temporal_query_feats \n        )\n        return temporal_query_feats\n    \n# v2 多层\nclass SS2D_FrameQuery_v2(nn.Module):\n    def __init__(self, configs,):\n        super().__init__()\n        d_model = configs['d_model']\n        self.homo = SS2D(d_model=configs['d_model'],\n                        d_state=configs['d_state'] if 'd_state' in configs else 16,\n                        d_conv=configs['d_conv'] if 'd_conv' in configs else 3,\n                        expand=configs['expand'] if 'expand' in configs else 2,\n                        dt_rank=configs['dt_rank'] if 'dt_rank' in configs else 'auto',\n                        dt_min=configs['dt_min'] if 'dt_min' in configs else 0.001,\n                        dt_max=configs['dt_max'] if 'dt_max' in configs else 0.1,\n                        dt_init=configs['dt_init'] if 'dt_init' in configs else 'random',\n                        dt_scale=configs['dt_scale'] if 'dt_scale' in configs else 1.0,\n                        dt_init_floor=configs['dt_init_floor'] if 'dt_init_floor' in configs else 1e-4,\n                        dropout=configs['dropout'] if 'dropout' in configs else 0,\n                        conv_bias=configs['conv_bias'] if 'conv_bias' in configs else True,\n                        bias=configs['bias'] if 'bias' in configs else False,\n                        )\n        self.pos_1d = build_position_encoding(position_embedding_name='1d')  # t上的position embedding\n        self.norm = nn.LayerNorm(d_model)\n        self.dropout = nn.Dropout(configs['dropout'])\n\n    def forward(self, \n                frame_query_feats=None,  # n bt c  \n                frame_query_poses=None, # n bt c  # nq上的Position embedding\n                nf=None,\n                **kwargs\n                ):\n        batch_size = frame_query_feats.shape[1] // nf # b\n        tgt2 = frame_query_feats + frame_query_poses  \n        tgt2 = rearrange(tgt2, 'n (b t) c -> b t n c',b=batch_size,t=nf).contiguous()\n\n        sin_poses = self.pos_1d(torch.zeros_like(tgt2[..., 0].permute(0, 2, 1).flatten(0, 1)).bool(), \n                                hidden_dim=tgt2.shape[-1]) # bn c t\n        sin_poses = rearrange(sin_poses, '(b n) c t -> b t n c', b=batch_size)\n        tgt2 += sin_poses\n\n        tgt2 = self.homo(tgt2) # b t n c\n        \n        tgt2 = tgt2.permute(2, 0, 1, 3).flatten(1, 2).contiguous() # n bt c\n\n\n        frame_query_feats = frame_query_feats + self.dropout(tgt2)\n        frame_query_feats = self.norm(frame_query_feats)\n\n        return frame_query_feats, None\n    \nfrom models.layers.utils import _get_clones\n@META_ARCH_REGISTRY.register()\nclass FrameQuery_SS2DLayer_v2(nn.Module):\n    def __init__(self, \n                 configs, \n                 dropout=0.0):\n        super().__init__()\n        d_model = configs['d_model']\n        n_layers = configs['nlayers'] if 'nlayers' in configs else 1\n        self.nlayers = n_layers\n        self.self_attn = _get_clones(SS2D_FrameQuery_v2(configs), n_layers)\n\n        from models.layers.decoder_layers import FFNLayer\n        self.ffn = FFNLayer(d_model=d_model,\n                            dim_feedforward=configs['dim_feedforward'],\n                            dropout=configs['dropout'],)\n\n    def forward(self, \n                frame_query_feats,  # n bt c  \n                frame_query_poses, # n bt c  # nq上的Position embedding\n                nf=None,\n                **kwargs):\n        \n        for i in range(self.nlayers):\n            frame_query_feats = self.self_attn[i](frame_query_feats=frame_query_feats,  # n bt c  \n                                                  frame_query_poses=frame_query_poses, # n bt c  # nq上的Position embedding\n                                                  nf=nf,)[0]\n            \n        frame_query_feats = self.ffn(frame_query_feats)\n        return frame_query_feats\n\n\nclass Hilbert_2DSelectiveScan(nn.Module):\n    def __init__(\n        self,\n        d_model,\n        d_state=16,\n        # d_state=\"auto\", # 20240109\n        d_conv=3,\n        expand=2,\n        dt_rank=\"auto\",\n        dt_min=0.001,\n        dt_max=0.1,\n        dt_init=\"random\",\n        dt_scale=1.0,\n        dt_init_floor=1e-4,\n        dropout=0.,\n        conv_bias=True,\n        bias=False,\n        device=None,\n        dtype=None,\n        scan_order=None,\n        **kwargs,\n    ):\n        factory_kwargs = {\"device\": device, \"dtype\": dtype}\n        super().__init__()\n        self.d_model = d_model\n        self.d_state = d_state\n        # self.d_state = math.ceil(self.d_model / 6) if d_state == \"auto\" else d_model # 20240109\n        self.d_conv = d_conv\n        self.expand = expand\n        self.d_inner = int(self.expand * self.d_model)\n        self.dt_rank = math.ceil(self.d_model / 16) if dt_rank == \"auto\" else dt_rank\n\n        self.in_proj = nn.Linear(self.d_model, self.d_inner * 2, bias=bias, **factory_kwargs)\n        self.conv2d = nn.Conv2d(\n            in_channels=self.d_inner,\n            out_channels=self.d_inner,\n            groups=self.d_inner,\n            bias=conv_bias,\n            kernel_size=d_conv,\n            padding=(d_conv - 1) // 2,\n            **factory_kwargs,\n        )\n        self.act = nn.SiLU()\n\n        self.x_proj = (\n            nn.Linear(self.d_inner, (self.dt_rank + self.d_state * 2), bias=False, **factory_kwargs), \n            nn.Linear(self.d_inner, (self.dt_rank + self.d_state * 2), bias=False, **factory_kwargs), \n        )\n        self.x_proj_weight = nn.Parameter(torch.stack([t.weight for t in self.x_proj], dim=0)) # (K=2, N, inner)\n        del self.x_proj\n\n        self.dt_projs = (\n            self.dt_init(self.dt_rank, self.d_inner, dt_scale, dt_init, dt_min, dt_max, dt_init_floor, **factory_kwargs),\n            self.dt_init(self.dt_rank, self.d_inner, dt_scale, dt_init, dt_min, dt_max, dt_init_floor, **factory_kwargs),\n        )\n        self.dt_projs_weight = nn.Parameter(torch.stack([t.weight for t in self.dt_projs], dim=0)) # (K=2, inner, rank)\n        self.dt_projs_bias = nn.Parameter(torch.stack([t.bias for t in self.dt_projs], dim=0)) # (K=2, inner)\n        del self.dt_projs\n        \n        self.A_logs = self.A_log_init(self.d_state, self.d_inner, copies=2, merge=True) # (K=2, D, N)\n        self.Ds = self.D_init(self.d_inner, copies=2, merge=True) # (K=2, D, N)\n\n        # self.selective_scan = selective_scan_fn\n        self.forward_core = self.forward_corev0\n\n        self.out_norm = nn.LayerNorm(self.d_inner)\n        self.out_proj = nn.Linear(self.d_inner, self.d_model, bias=bias, **factory_kwargs)\n        self.dropout = nn.Dropout(dropout) if dropout > 0. else None\n        self.scan_order = scan_order\n\n    @staticmethod\n    def dt_init(dt_rank, d_inner, dt_scale=1.0, dt_init=\"random\", dt_min=0.001, dt_max=0.1, dt_init_floor=1e-4, **factory_kwargs):\n        dt_proj = nn.Linear(dt_rank, d_inner, bias=True, **factory_kwargs)\n\n        # Initialize special dt projection to preserve variance at initialization\n        dt_init_std = dt_rank**-0.5 * dt_scale\n        if dt_init == \"constant\":\n            nn.init.constant_(dt_proj.weight, dt_init_std)\n        elif dt_init == \"random\":\n            nn.init.uniform_(dt_proj.weight, -dt_init_std, dt_init_std)\n        else:\n            raise NotImplementedError\n\n        # Initialize dt bias so that F.softplus(dt_bias) is between dt_min and dt_max\n        dt = torch.exp(\n            torch.rand(d_inner, **factory_kwargs) * (math.log(dt_max) - math.log(dt_min))\n            + math.log(dt_min)\n        ).clamp(min=dt_init_floor)\n        # Inverse of softplus: https://github.com/pytorch/pytorch/issues/72759\n        inv_dt = dt + torch.log(-torch.expm1(-dt))\n        with torch.no_grad():\n            dt_proj.bias.copy_(inv_dt)\n        # Our initialization would set all Linear.bias to zero, need to mark this one as _no_reinit\n        dt_proj.bias._no_reinit = True\n        \n        return dt_proj\n\n    @staticmethod\n    def A_log_init(d_state, d_inner, copies=1, device=None, merge=True):\n        # S4D real initialization\n        A = repeat(\n            torch.arange(1, d_state + 1, dtype=torch.float32, device=device),\n            \"n -> d n\",\n            d=d_inner,\n        ).contiguous()\n        A_log = torch.log(A)  # Keep A_log in fp32\n        if copies > 1:\n            A_log = repeat(A_log, \"d n -> r d n\", r=copies)\n            if merge:\n                A_log = A_log.flatten(0, 1)\n        A_log = nn.Parameter(A_log)\n        A_log._no_weight_decay = True\n        return A_log\n\n    @staticmethod\n    def D_init(d_inner, copies=1, device=None, merge=True):\n        # D \"skip\" parameter\n        D = torch.ones(d_inner, device=device)\n        if copies > 1:\n            D = repeat(D, \"n1 -> r n1\", r=copies)\n            if merge:\n                D = D.flatten(0, 1)\n        D = nn.Parameter(D)  # Keep in fp32\n        D._no_weight_decay = True\n        return D\n\n    def forward_corev0(self, x: torch.Tensor, hilbert_curve):\n        # LongTensor[int] 按照hw进行flatten之后的hilbert排序\n        self.selective_scan = selective_scan_fn\n                \n        B, C, H, W = x.shape\n        L = H * W\n        K = 2\n\n        if self.scan_order == 'zigzag':\n            x_hw = x.view(B, -1, L).contiguous() # b c hw\n            xs = torch.stack([x_hw, torch.flip(x_hw, dims=[-1])], dim=1) # (b, k, d, l)\n        elif self.scan_order == 'hilbert':\n            x_hw = x.flatten(2).contiguous() # b c hw\n            x_hil = x_hw.index_select(dim=-1, index=hilbert_curve)\n            xs = torch.stack([x_hil, torch.flip(x_hil, dims=[-1])], dim=1) # (b, k, d, l)\n        else:\n            raise ValueError()\n\n        x_dbl = torch.einsum(\"b k d l, k c d -> b k c l\", xs.view(B, K, -1, L), self.x_proj_weight)\n        # x_dbl = x_dbl + self.x_proj_bias.view(1, K, -1, 1)\n        dts, Bs, Cs = torch.split(x_dbl, [self.dt_rank, self.d_state, self.d_state], dim=2)\n        dts = torch.einsum(\"b k r l, k d r -> b k d l\", dts.view(B, K, -1, L), self.dt_projs_weight)\n        # dts = dts + self.dt_projs_bias.view(1, K, -1, 1)\n\n        xs = xs.float().view(B, -1, L) # (b, k * d, l)\n        dts = dts.contiguous().float().view(B, -1, L) # (b, k * d, l)\n        Bs = Bs.float().view(B, K, -1, L) # (b, k, d_state, l)\n        Cs = Cs.float().view(B, K, -1, L) # (b, k, d_state, l)\n        Ds = self.Ds.float().view(-1) # (k * d)\n        As = -torch.exp(self.A_logs.float()).view(-1, self.d_state)  # (k * d, d_state)\n        dt_projs_bias = self.dt_projs_bias.float().view(-1) # (k * d)\n\n        out_y = self.selective_scan(\n            xs, dts, \n            As, Bs, Cs, Ds, z=None,\n            delta_bias=dt_projs_bias,\n            delta_softplus=True,\n            return_last_state=False,\n        ).view(B, K, -1, L)\n\n        assert out_y.dtype == torch.float\n\n        if self.scan_order == 'zigzag':\n            hw_order = out_y[:, 0].contiguous().view(B, -1, H, W).contiguous()\n            rhw_order = torch.flip(out_y[:, 1].contiguous(), dims=[-1]).contiguous()\n            rhw_order = rhw_order.view(B, -1, H, W,).contiguous()\n            return hw_order + rhw_order\n        \n        elif self.scan_order == 'hilbert':\n            hil_order = out_y[:, 0].contiguous() # b c hw\n            rhil_order = torch.flip(out_y[:, 1].contiguous(), dims=[-1]).contiguous() # b c hw\n\n            sum_out = torch.zeros_like(hil_order)\n            hilbert_curve = repeat(hilbert_curve, 'hw -> b c hw', b=hil_order.shape[0], c=hil_order.shape[1])\n            assert hil_order.shape == hilbert_curve.shape\n            sum_out.scatter_add_(dim=-1, index=hilbert_curve, src=hil_order)\n            sum_out.scatter_add_(dim=-1, index=hilbert_curve, src=rhil_order)\n            sum_out = sum_out.view(B, -1, H, W).contiguous()\n            return sum_out\n\n\n    # def forward_corev0(self, x: torch.Tensor, hilbert_curve):\n    #     # LongTensor[int] 按照hw进行flatten之后的hilbert排序\n    #     self.selective_scan = selective_scan_fn\n                \n    #     B, C, H, W, T = x.shape\n    #     L = H * W * T\n    #     K = 2\n\n    #     if self.scan_order == 'zigzag':\n    #         x_hw = x.view(B, -1, L).contiguous() # b c hwt\n    #         xs = torch.stack([x_hw, torch.flip(x_hw, dims=[-1])], dim=1) # (b, k, d, l)\n    #     elif self.scan_order == 'hilbert':\n    #         x_hw = x.flatten(2).contiguous() # b c hwt\n    #         x_hil = x_hw.index_select(dim=-1, index=hilbert_curve)\n    #         xs = torch.stack([x_hil, torch.flip(x_hil, dims=[-1])], dim=1) # (b, k, d, l)\n    #     else:\n    #         raise ValueError()\n\n    #     x_dbl = torch.einsum(\"b k d l, k c d -> b k c l\", xs.view(B, K, -1, L), self.x_proj_weight)\n    #     # x_dbl = x_dbl + self.x_proj_bias.view(1, K, -1, 1)\n    #     dts, Bs, Cs = torch.split(x_dbl, [self.dt_rank, self.d_state, self.d_state], dim=2)\n    #     dts = torch.einsum(\"b k r l, k d r -> b k d l\", dts.view(B, K, -1, L), self.dt_projs_weight)\n    #     # dts = dts + self.dt_projs_bias.view(1, K, -1, 1)\n\n    #     xs = xs.float().view(B, -1, L) # (b, k * d, l)\n    #     dts = dts.contiguous().float().view(B, -1, L) # (b, k * d, l)\n    #     Bs = Bs.float().view(B, K, -1, L) # (b, k, d_state, l)\n    #     Cs = Cs.float().view(B, K, -1, L) # (b, k, d_state, l)\n    #     Ds = self.Ds.float().view(-1) # (k * d)\n    #     As = -torch.exp(self.A_logs.float()).view(-1, self.d_state)  # (k * d, d_state)\n    #     dt_projs_bias = self.dt_projs_bias.float().view(-1) # (k * d)\n\n    #     out_y = self.selective_scan(\n    #         xs, dts, \n    #         As, Bs, Cs, Ds, z=None,\n    #         delta_bias=dt_projs_bias,\n    #         delta_softplus=True,\n    #         return_last_state=False,\n    #     ).view(B, K, -1, L)\n\n    #     assert out_y.dtype == torch.float\n\n    #     if self.scan_order == 'zigzag':\n    #         hw_order = out_y[:, 0].contiguous().view(B, -1, H, W).contiguous()\n    #         rhw_order = torch.flip(out_y[:, 1].contiguous(), dims=[-1]).contiguous()\n    #         rhw_order = rhw_order.view(B, -1, H, W,).contiguous()\n    #         return hw_order + rhw_order\n        \n    #     elif self.scan_order == 'hilbert':\n    #         hil_order = out_y[:, 0].contiguous() # b c hw\n    #         rhil_order = torch.flip(out_y[:, 1].contiguous(), dims=[-1]).contiguous() # b c hw\n\n    #         sum_out = torch.zeros_like(hil_order)\n    #         hilbert_curve = repeat(hilbert_curve, 'hwt -> b c hwt', b=hil_order.shape[0], c=hil_order.shape[1])\n    #         assert hil_order.shape == hilbert_curve.shape\n    #         sum_out.scatter_add_(dim=-1, index=hilbert_curve, src=hil_order)\n    #         sum_out.scatter_add_(dim=-1, index=hilbert_curve, src=rhil_order)\n    #         sum_out = sum_out.view(B, -1, H, W).contiguous()\n    #         return sum_out\n\n\n    def forward(self, x: torch.Tensor, hilbert_curve, **kwargs):\n\n        B, H, W, C = x.shape\n        xz = self.in_proj(x)\n        x, z = xz.chunk(2, dim=-1) # (b, h, w, d)\n\n        x = x.permute(0, 3, 1, 2).contiguous()\n        x = self.act(self.conv2d(x)) # (b, d, h, w)\n        \n        y  = self.forward_core(x, hilbert_curve=hilbert_curve) # B C h w\n        y = y.permute(0, 2, 3, 1).contiguous() # b h w c\n        y = self.out_norm(y)\n        y = y * F.silu(z)\n        out = self.out_proj(y)\n        if self.dropout is not None:\n            out = self.dropout(out)\n        return out\n\n\nclass SS2D_FrameQuery_hilbert(nn.Module):\n    def __init__(self, configs,):\n        super().__init__()\n        d_model = configs['d_model']\n        self.homo = Hilbert_2DSelectiveScan(d_model=configs['d_model'],\n                        d_state=configs['d_state'] if 'd_state' in configs else 16,\n                        d_conv=configs['d_conv'] if 'd_conv' in configs else 3,\n                        expand=configs['expand'] if 'expand' in configs else 2,\n                        dt_rank=configs['dt_rank'] if 'dt_rank' in configs else 'auto',\n                        dt_min=configs['dt_min'] if 'dt_min' in configs else 0.001,\n                        dt_max=configs['dt_max'] if 'dt_max' in configs else 0.1,\n                        dt_init=configs['dt_init'] if 'dt_init' in configs else 'random',\n                        dt_scale=configs['dt_scale'] if 'dt_scale' in configs else 1.0,\n                        dt_init_floor=configs['dt_init_floor'] if 'dt_init_floor' in configs else 1e-4,\n                        dropout=configs['dropout'] if 'dropout' in configs else 0,\n                        conv_bias=configs['conv_bias'] if 'conv_bias' in configs else True,\n                        bias=configs['bias'] if 'bias' in configs else False,\n                        scan_order=configs['scan_order']\n                        )\n        self.pos_1d = build_position_encoding(position_embedding_name='1d')  # t上的position embedding\n        self.norm = nn.LayerNorm(d_model)\n        self.dropout = nn.Dropout(configs['dropout'])\n\n    def forward(self, \n                frame_query_feats=None,  # n bt c  \n                frame_query_poses=None, # n bt c  # nq上的Position embedding\n                hilbert_curve=None,\n                nf=None,\n                **kwargs\n                ):\n        batch_size = frame_query_feats.shape[1] // nf # b\n        tgt2 = frame_query_feats + frame_query_poses  \n        tgt2 = rearrange(tgt2, 'n (b t) c -> b t n c',b=batch_size,t=nf).contiguous()\n\n        sin_poses = self.pos_1d(torch.zeros_like(tgt2[..., 0].permute(0, 2, 1).flatten(0, 1)).bool(), \n                                hidden_dim=tgt2.shape[-1]) # bn c t\n        sin_poses = rearrange(sin_poses, '(b n) c t -> b t n c', b=batch_size)\n        tgt2 += sin_poses\n\n        tgt2 = self.homo(tgt2, hilbert_curve=hilbert_curve) # b t n c\n        \n        tgt2 = tgt2.permute(2, 0, 1, 3).flatten(1, 2).contiguous() # n bt c\n\n\n        frame_query_feats = frame_query_feats + self.dropout(tgt2)\n        frame_query_feats = self.norm(frame_query_feats)\n\n        return frame_query_feats, None\n    \nfrom models.layers.utils import _get_clones\n\n@META_ARCH_REGISTRY.register()\nclass FrameQuery_SS2DLayer_hilbert(nn.Module):\n    def __init__(self, \n                 configs, \n                 dropout=0.0):\n        super().__init__()\n        d_model = configs['d_model']\n        n_layers = configs['nlayers'] if 'nlayers' in configs else 1\n        self.nlayers = n_layers\n        self.self_attn = _get_clones(SS2D_FrameQuery_hilbert(configs), n_layers)\n        from models.layers.decoder_layers import FFNLayer\n        self.ffn = FFNLayer(d_model=d_model,\n                            dim_feedforward=configs['dim_feedforward'],\n                            dropout=configs['dropout'],)\n\n    def forward(self, \n                frame_query_feats,  # n bt c  \n                frame_query_poses, # n bt c  # nq上的Position embedding\n                video_aux_dict=None,\n                **kwargs):\n\n        for i in range(self.nlayers):\n            frame_query_feats = self.self_attn[i](frame_query_feats=frame_query_feats,  # n bt c  \n                                                  frame_query_poses=frame_query_poses, # n bt c  # nq上的Position embedding\n                                                  nf=video_aux_dict['nf'],\n                                                  hilbert_curve=video_aux_dict['hilbert_curve'])[0]\n        frame_query_feats = self.ffn(frame_query_feats)\n        return frame_query_feats\n\n\n\n"
  },
  {
    "path": "models/encoder/ops/modules/ms_deform_attn.py",
    "content": "# Modify for sample points visualization\n# ------------------------------------------------------------------------------------------------\n# Deformable DETR\n# Copyright (c) 2020 SenseTime. All Rights Reserved.\n# Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n# ------------------------------------------------------------------------------------------------\n# Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n# ------------------------------------------------------------------------------------------------\n\nfrom __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import division\n\nimport warnings\nimport math\n\nimport torch\nfrom torch import nn\nimport torch.nn.functional as F\nfrom torch.nn.init import xavier_uniform_, constant_\n\nfrom ..functions import MSDeformAttnFunction\n\n\ndef _is_power_of_2(n):\n    if (not isinstance(n, int)) or (n < 0):\n        raise ValueError(\"invalid input for _is_power_of_2: {} (type: {})\".format(n, type(n)))\n    return (n & (n-1) == 0) and n != 0\n\n\nclass MSDeformAttn(nn.Module):\n    def __init__(self, d_model=256, n_levels=4, n_heads=8, n_points=4):\n        \"\"\"\n        Multi-Scale Deformable Attention Module\n        :param d_model      hidden dimension\n        :param n_levels     number of feature levels\n        :param n_heads      number of attention heads\n        :param n_points     number of sampling points per attention head per feature level\n        \"\"\"\n        super().__init__()\n        if d_model % n_heads != 0:\n            raise ValueError('d_model must be divisible by n_heads, but got {} and {}'.format(d_model, n_heads))\n        _d_per_head = d_model // n_heads\n        # you'd better set _d_per_head to a power of 2 which is more efficient in our CUDA implementation\n        if not _is_power_of_2(_d_per_head):\n            warnings.warn(\"You'd better set d_model in MSDeformAttn to make the dimension of each attention head a power of 2 \"\n                          \"which is more efficient in our CUDA implementation.\")\n\n        self.im2col_step = 512\n\n        self.d_model = d_model\n        self.n_levels = n_levels\n        self.n_heads = n_heads\n        self.n_points = n_points\n\n        self.sampling_offsets = nn.Linear(d_model, n_heads * n_levels * n_points * 2)\n        self.attention_weights = nn.Linear(d_model, n_heads * n_levels * n_points)\n        self.value_proj = nn.Linear(d_model, d_model)\n        self.output_proj = nn.Linear(d_model, d_model)\n\n        self._reset_parameters()\n\n    def _reset_parameters(self):\n        constant_(self.sampling_offsets.weight.data, 0.)\n        thetas = torch.arange(self.n_heads, dtype=torch.float32) * (2.0 * math.pi / self.n_heads)\n        grid_init = torch.stack([thetas.cos(), thetas.sin()], -1)\n        grid_init = (grid_init / grid_init.abs().max(-1, keepdim=True)[0]).view(self.n_heads, 1, 1, 2).repeat(1, self.n_levels, self.n_points, 1)\n        for i in range(self.n_points):\n            grid_init[:, :, i, :] *= i + 1\n        with torch.no_grad():\n            self.sampling_offsets.bias = nn.Parameter(grid_init.view(-1))\n        constant_(self.attention_weights.weight.data, 0.)\n        constant_(self.attention_weights.bias.data, 0.)\n        xavier_uniform_(self.value_proj.weight.data)\n        constant_(self.value_proj.bias.data, 0.)\n        xavier_uniform_(self.output_proj.weight.data)\n        constant_(self.output_proj.bias.data, 0.)\n\n    def forward(self, query, reference_points, input_flatten, input_spatial_shapes, input_level_start_index, input_padding_mask=None):\n        \"\"\"\n        :param query                       (N, Length_{query}, C)\n        :param reference_points            (N, Length_{query}, n_levels, 2), range in [0, 1], top-left (0,0), bottom-right (1, 1), including padding area\n                                        or (N, Length_{query}, n_levels, 4), add additional (w, h) to form reference boxes\n        :param input_flatten               (N, \\sum_{l=0}^{L-1} H_l \\cdot W_l, C)\n        :param input_spatial_shapes        (n_levels, 2), [(H_0, W_0), (H_1, W_1), ..., (H_{L-1}, W_{L-1})]\n        :param input_level_start_index     (n_levels, ), [0, H_0*W_0, H_0*W_0+H_1*W_1, H_0*W_0+H_1*W_1+H_2*W_2, ..., H_0*W_0+H_1*W_1+...+H_{L-1}*W_{L-1}]\n        :param input_padding_mask          (N, \\sum_{l=0}^{L-1} H_l \\cdot W_l), True for padding elements, False for non-padding elements\n\n        :return output                     (N, Length_{query}, C)\n        \"\"\"\n        N, Len_q, _ = query.shape\n        N, Len_in, _ = input_flatten.shape\n        assert (input_spatial_shapes[:, 0] * input_spatial_shapes[:, 1]).sum() == Len_in\n\n        value = self.value_proj(input_flatten)\n        if input_padding_mask is not None:\n            value = value.masked_fill(input_padding_mask[..., None], float(0))\n        value = value.view(N, Len_in, self.n_heads, self.d_model // self.n_heads)\n        sampling_offsets = self.sampling_offsets(query).view(N, Len_q, self.n_heads, self.n_levels, self.n_points, 2)\n        attention_weights = self.attention_weights(query).view(N, Len_q, self.n_heads, self.n_levels * self.n_points)\n        attention_weights = F.softmax(attention_weights, -1).view(N, Len_q, self.n_heads, self.n_levels, self.n_points)\n        # N, Len_q, n_heads, n_levels, n_points, 2\n        if reference_points.shape[-1] == 2:\n            offset_normalizer = torch.stack([input_spatial_shapes[..., 1], input_spatial_shapes[..., 0]], -1)\n            sampling_locations = reference_points[:, :, None, :, None, :] \\\n                                 + sampling_offsets / offset_normalizer[None, None, None, :, None, :]\n        elif reference_points.shape[-1] == 4:\n            sampling_locations = reference_points[:, :, None, :, None, :2] \\\n                                 + sampling_offsets / self.n_points * reference_points[:, :, None, :, None, 2:] * 0.5\n        else:\n            raise ValueError(\n                'Last dim of reference_points must be 2 or 4, but get {} instead.'.format(reference_points.shape[-1]))\n        output = MSDeformAttnFunction.apply(\n            value, input_spatial_shapes, input_level_start_index, sampling_locations, attention_weights, self.im2col_step)\n        output = self.output_proj(output)\n\n        return output, sampling_locations, attention_weights\n"
  },
  {
    "path": "models/encoder/ops/setup.py",
    "content": "# ------------------------------------------------------------------------------------------------\n# Deformable DETR\n# Copyright (c) 2020 SenseTime. All Rights Reserved.\n# Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n# ------------------------------------------------------------------------------------------------\n# Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n# ------------------------------------------------------------------------------------------------\n\nimport os\nimport glob\n\nimport torch\n\nfrom torch.utils.cpp_extension import CUDA_HOME\nfrom torch.utils.cpp_extension import CppExtension\nfrom torch.utils.cpp_extension import CUDAExtension\n\nfrom setuptools import find_packages\nfrom setuptools import setup\n\nrequirements = [\"torch\", \"torchvision\"]\n\ndef get_extensions():\n    this_dir = os.path.dirname(os.path.abspath(__file__))\n    extensions_dir = os.path.join(this_dir, \"src\")\n\n    main_file = glob.glob(os.path.join(extensions_dir, \"*.cpp\"))\n    source_cpu = glob.glob(os.path.join(extensions_dir, \"cpu\", \"*.cpp\"))\n    source_cuda = glob.glob(os.path.join(extensions_dir, \"cuda\", \"*.cu\"))\n\n    sources = main_file + source_cpu\n    extension = CppExtension\n    extra_compile_args = {\"cxx\": []}\n    define_macros = []\n\n    if torch.cuda.is_available() and CUDA_HOME is not None:\n        extension = CUDAExtension\n        sources += source_cuda\n        define_macros += [(\"WITH_CUDA\", None)]\n        extra_compile_args[\"nvcc\"] = [\n            \"-DCUDA_HAS_FP16=1\",\n            \"-D__CUDA_NO_HALF_OPERATORS__\",\n            \"-D__CUDA_NO_HALF_CONVERSIONS__\",\n            \"-D__CUDA_NO_HALF2_OPERATORS__\",\n        ]\n    else:\n        raise NotImplementedError('Cuda is not availabel')\n\n    sources = [os.path.join(extensions_dir, s) for s in sources]\n    include_dirs = [extensions_dir]\n    ext_modules = [\n        extension(\n            \"MultiScaleDeformableAttention\",\n            sources,\n            include_dirs=include_dirs,\n            define_macros=define_macros,\n            extra_compile_args=extra_compile_args,\n        )\n    ]\n    return ext_modules\n\nsetup(\n    name=\"MultiScaleDeformableAttention\",\n    version=\"1.0\",\n    author=\"Weijie Su\",\n    url=\"https://github.com/fundamentalvision/Deformable-DETR\",\n    description=\"PyTorch Wrapper for CUDA Functions of Multi-Scale Deformable Attention\",\n    packages=find_packages(exclude=(\"configs\", \"tests\",)),\n    ext_modules=get_extensions(),\n    cmdclass={\"build_ext\": torch.utils.cpp_extension.BuildExtension},\n)\n"
  },
  {
    "path": "models/encoder/ops/src/cpu/ms_deform_attn_cpu.cpp",
    "content": "/*!\n**************************************************************************************************\n* Deformable DETR\n* Copyright (c) 2020 SenseTime. All Rights Reserved.\n* Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n**************************************************************************************************\n* Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n**************************************************************************************************\n*/\n\n#include <vector>\n\n#include <ATen/ATen.h>\n#include <ATen/cuda/CUDAContext.h>\n\n\nat::Tensor\nms_deform_attn_cpu_forward(\n    const at::Tensor &value, \n    const at::Tensor &spatial_shapes,\n    const at::Tensor &level_start_index,\n    const at::Tensor &sampling_loc,\n    const at::Tensor &attn_weight,\n    const int im2col_step)\n{\n    AT_ERROR(\"Not implement on cpu\");\n}\n\nstd::vector<at::Tensor>\nms_deform_attn_cpu_backward(\n    const at::Tensor &value, \n    const at::Tensor &spatial_shapes,\n    const at::Tensor &level_start_index,\n    const at::Tensor &sampling_loc,\n    const at::Tensor &attn_weight,\n    const at::Tensor &grad_output,\n    const int im2col_step)\n{\n    AT_ERROR(\"Not implement on cpu\");\n}\n\n"
  },
  {
    "path": "models/encoder/ops/src/cpu/ms_deform_attn_cpu.h",
    "content": "/*!\n**************************************************************************************************\n* Deformable DETR\n* Copyright (c) 2020 SenseTime. All Rights Reserved.\n* Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n**************************************************************************************************\n* Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n**************************************************************************************************\n*/\n\n#pragma once\n#include <torch/extension.h>\n\nat::Tensor\nms_deform_attn_cpu_forward(\n    const at::Tensor &value, \n    const at::Tensor &spatial_shapes,\n    const at::Tensor &level_start_index,\n    const at::Tensor &sampling_loc,\n    const at::Tensor &attn_weight,\n    const int im2col_step);\n\nstd::vector<at::Tensor>\nms_deform_attn_cpu_backward(\n    const at::Tensor &value, \n    const at::Tensor &spatial_shapes,\n    const at::Tensor &level_start_index,\n    const at::Tensor &sampling_loc,\n    const at::Tensor &attn_weight,\n    const at::Tensor &grad_output,\n    const int im2col_step);\n\n\n"
  },
  {
    "path": "models/encoder/ops/src/cuda/ms_deform_attn_cuda.cu",
    "content": "/*!\n**************************************************************************************************\n* Deformable DETR\n* Copyright (c) 2020 SenseTime. All Rights Reserved.\n* Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n**************************************************************************************************\n* Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n**************************************************************************************************\n*/\n\n#include <vector>\n#include \"cuda/ms_deform_im2col_cuda.cuh\"\n\n#include <ATen/ATen.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <cuda.h>\n#include <cuda_runtime.h>\n\n\nat::Tensor ms_deform_attn_cuda_forward(\n    const at::Tensor &value, \n    const at::Tensor &spatial_shapes,\n    const at::Tensor &level_start_index,\n    const at::Tensor &sampling_loc,\n    const at::Tensor &attn_weight,\n    const int im2col_step)\n{\n    AT_ASSERTM(value.is_contiguous(), \"value tensor has to be contiguous\");\n    AT_ASSERTM(spatial_shapes.is_contiguous(), \"spatial_shapes tensor has to be contiguous\");\n    AT_ASSERTM(level_start_index.is_contiguous(), \"level_start_index tensor has to be contiguous\");\n    AT_ASSERTM(sampling_loc.is_contiguous(), \"sampling_loc tensor has to be contiguous\");\n    AT_ASSERTM(attn_weight.is_contiguous(), \"attn_weight tensor has to be contiguous\");\n\n    AT_ASSERTM(value.type().is_cuda(), \"value must be a CUDA tensor\");\n    AT_ASSERTM(spatial_shapes.type().is_cuda(), \"spatial_shapes must be a CUDA tensor\");\n    AT_ASSERTM(level_start_index.type().is_cuda(), \"level_start_index must be a CUDA tensor\");\n    AT_ASSERTM(sampling_loc.type().is_cuda(), \"sampling_loc must be a CUDA tensor\");\n    AT_ASSERTM(attn_weight.type().is_cuda(), \"attn_weight must be a CUDA tensor\");\n\n    const int batch = value.size(0);\n    const int spatial_size = value.size(1);\n    const int num_heads = value.size(2);\n    const int channels = value.size(3);\n\n    const int num_levels = spatial_shapes.size(0);\n\n    const int num_query = sampling_loc.size(1);\n    const int num_point = sampling_loc.size(4);\n\n    const int im2col_step_ = std::min(batch, im2col_step);\n\n    AT_ASSERTM(batch % im2col_step_ == 0, \"batch(%d) must divide im2col_step(%d)\", batch, im2col_step_);\n    \n    auto output = at::zeros({batch, num_query, num_heads, channels}, value.options());\n\n    const int batch_n = im2col_step_;\n    auto output_n = output.view({batch/im2col_step_, batch_n, num_query, num_heads, channels});\n    auto per_value_size = spatial_size * num_heads * channels;\n    auto per_sample_loc_size = num_query * num_heads * num_levels * num_point * 2;\n    auto per_attn_weight_size = num_query * num_heads * num_levels * num_point;\n    for (int n = 0; n < batch/im2col_step_; ++n)\n    {\n        auto columns = output_n.select(0, n);\n        AT_DISPATCH_FLOATING_TYPES(value.type(), \"ms_deform_attn_forward_cuda\", ([&] {\n            ms_deformable_im2col_cuda(at::cuda::getCurrentCUDAStream(),\n                value.data<scalar_t>() + n * im2col_step_ * per_value_size,\n                spatial_shapes.data<int64_t>(),\n                level_start_index.data<int64_t>(),\n                sampling_loc.data<scalar_t>() + n * im2col_step_ * per_sample_loc_size,\n                attn_weight.data<scalar_t>() + n * im2col_step_ * per_attn_weight_size,\n                batch_n, spatial_size, num_heads, channels, num_levels, num_query, num_point,\n                columns.data<scalar_t>());\n\n        }));\n    }\n\n    output = output.view({batch, num_query, num_heads*channels});\n\n    return output;\n}\n\n\nstd::vector<at::Tensor> ms_deform_attn_cuda_backward(\n    const at::Tensor &value, \n    const at::Tensor &spatial_shapes,\n    const at::Tensor &level_start_index,\n    const at::Tensor &sampling_loc,\n    const at::Tensor &attn_weight,\n    const at::Tensor &grad_output,\n    const int im2col_step)\n{\n\n    AT_ASSERTM(value.is_contiguous(), \"value tensor has to be contiguous\");\n    AT_ASSERTM(spatial_shapes.is_contiguous(), \"spatial_shapes tensor has to be contiguous\");\n    AT_ASSERTM(level_start_index.is_contiguous(), \"level_start_index tensor has to be contiguous\");\n    AT_ASSERTM(sampling_loc.is_contiguous(), \"sampling_loc tensor has to be contiguous\");\n    AT_ASSERTM(attn_weight.is_contiguous(), \"attn_weight tensor has to be contiguous\");\n    AT_ASSERTM(grad_output.is_contiguous(), \"grad_output tensor has to be contiguous\");\n\n    AT_ASSERTM(value.type().is_cuda(), \"value must be a CUDA tensor\");\n    AT_ASSERTM(spatial_shapes.type().is_cuda(), \"spatial_shapes must be a CUDA tensor\");\n    AT_ASSERTM(level_start_index.type().is_cuda(), \"level_start_index must be a CUDA tensor\");\n    AT_ASSERTM(sampling_loc.type().is_cuda(), \"sampling_loc must be a CUDA tensor\");\n    AT_ASSERTM(attn_weight.type().is_cuda(), \"attn_weight must be a CUDA tensor\");\n    AT_ASSERTM(grad_output.type().is_cuda(), \"grad_output must be a CUDA tensor\");\n\n    const int batch = value.size(0);\n    const int spatial_size = value.size(1);\n    const int num_heads = value.size(2);\n    const int channels = value.size(3);\n\n    const int num_levels = spatial_shapes.size(0);\n\n    const int num_query = sampling_loc.size(1);\n    const int num_point = sampling_loc.size(4);\n\n    const int im2col_step_ = std::min(batch, im2col_step);\n\n    AT_ASSERTM(batch % im2col_step_ == 0, \"batch(%d) must divide im2col_step(%d)\", batch, im2col_step_);\n\n    auto grad_value = at::zeros_like(value);\n    auto grad_sampling_loc = at::zeros_like(sampling_loc);\n    auto grad_attn_weight = at::zeros_like(attn_weight);\n\n    const int batch_n = im2col_step_;\n    auto per_value_size = spatial_size * num_heads * channels;\n    auto per_sample_loc_size = num_query * num_heads * num_levels * num_point * 2;\n    auto per_attn_weight_size = num_query * num_heads * num_levels * num_point;\n    auto grad_output_n = grad_output.view({batch/im2col_step_, batch_n, num_query, num_heads, channels});\n    \n    for (int n = 0; n < batch/im2col_step_; ++n)\n    {\n        auto grad_output_g = grad_output_n.select(0, n);\n        AT_DISPATCH_FLOATING_TYPES(value.type(), \"ms_deform_attn_backward_cuda\", ([&] {\n            ms_deformable_col2im_cuda(at::cuda::getCurrentCUDAStream(),\n                                    grad_output_g.data<scalar_t>(),\n                                    value.data<scalar_t>() + n * im2col_step_ * per_value_size,\n                                    spatial_shapes.data<int64_t>(),\n                                    level_start_index.data<int64_t>(),\n                                    sampling_loc.data<scalar_t>() + n * im2col_step_ * per_sample_loc_size,\n                                    attn_weight.data<scalar_t>() + n * im2col_step_ * per_attn_weight_size,\n                                    batch_n, spatial_size, num_heads, channels, num_levels, num_query, num_point,\n                                    grad_value.data<scalar_t>() +  n * im2col_step_ * per_value_size,\n                                    grad_sampling_loc.data<scalar_t>() + n * im2col_step_ * per_sample_loc_size,\n                                    grad_attn_weight.data<scalar_t>() + n * im2col_step_ * per_attn_weight_size);\n\n        }));\n    }\n\n    return {\n        grad_value, grad_sampling_loc, grad_attn_weight\n    };\n}"
  },
  {
    "path": "models/encoder/ops/src/cuda/ms_deform_attn_cuda.h",
    "content": "/*!\n**************************************************************************************************\n* Deformable DETR\n* Copyright (c) 2020 SenseTime. All Rights Reserved.\n* Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n**************************************************************************************************\n* Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n**************************************************************************************************\n*/\n\n#pragma once\n#include <torch/extension.h>\n\nat::Tensor ms_deform_attn_cuda_forward(\n    const at::Tensor &value, \n    const at::Tensor &spatial_shapes,\n    const at::Tensor &level_start_index,\n    const at::Tensor &sampling_loc,\n    const at::Tensor &attn_weight,\n    const int im2col_step);\n\nstd::vector<at::Tensor> ms_deform_attn_cuda_backward(\n    const at::Tensor &value, \n    const at::Tensor &spatial_shapes,\n    const at::Tensor &level_start_index,\n    const at::Tensor &sampling_loc,\n    const at::Tensor &attn_weight,\n    const at::Tensor &grad_output,\n    const int im2col_step);\n\n"
  },
  {
    "path": "models/encoder/ops/src/cuda/ms_deform_im2col_cuda.cuh",
    "content": "/*!\n**************************************************************************\n* Deformable DETR\n* Copyright (c) 2020 SenseTime. All Rights Reserved.\n* Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n**************************************************************************\n* Modified from DCN (https://github.com/msracver/Deformable-ConvNets)\n* Copyright (c) 2018 Microsoft\n**************************************************************************\n*/\n\n#include <cstdio>\n#include <algorithm>\n#include <cstring>\n\n#include <ATen/ATen.h>\n#include <ATen/cuda/CUDAContext.h>\n\n#include <THC/THCAtomics.cuh>\n\n#define CUDA_KERNEL_LOOP(i, n)                          \\\n  for (int i = blockIdx.x * blockDim.x + threadIdx.x;   \\\n      i < (n);                                          \\\n      i += blockDim.x * gridDim.x)\n\nconst int CUDA_NUM_THREADS = 1024;\ninline int GET_BLOCKS(const int N, const int num_threads)\n{\n  return (N + num_threads - 1) / num_threads;\n}\n\n\ntemplate <typename scalar_t>\n__device__ scalar_t ms_deform_attn_im2col_bilinear(const scalar_t* &bottom_data, \n                                                   const int &height, const int &width, const int &nheads, const int &channels,\n                                                   const scalar_t &h, const scalar_t &w, const int &m, const int &c)\n{\n  const int h_low = floor(h);\n  const int w_low = floor(w);\n  const int h_high = h_low + 1;\n  const int w_high = w_low + 1;\n\n  const scalar_t lh = h - h_low;\n  const scalar_t lw = w - w_low;\n  const scalar_t hh = 1 - lh, hw = 1 - lw;\n\n  const int w_stride = nheads * channels;\n  const int h_stride = width * w_stride;\n  const int h_low_ptr_offset = h_low * h_stride;\n  const int h_high_ptr_offset = h_low_ptr_offset + h_stride;\n  const int w_low_ptr_offset = w_low * w_stride;\n  const int w_high_ptr_offset = w_low_ptr_offset + w_stride;\n  const int base_ptr = m * channels + c;\n\n  scalar_t v1 = 0;\n  if (h_low >= 0 && w_low >= 0)\n  {\n    const int ptr1 = h_low_ptr_offset + w_low_ptr_offset + base_ptr;\n    v1 = bottom_data[ptr1];\n  }\n  scalar_t v2 = 0;\n  if (h_low >= 0 && w_high <= width - 1)\n  {\n    const int ptr2 = h_low_ptr_offset + w_high_ptr_offset + base_ptr;\n    v2 = bottom_data[ptr2];\n  }\n  scalar_t v3 = 0;\n  if (h_high <= height - 1 && w_low >= 0)\n  {\n    const int ptr3 = h_high_ptr_offset + w_low_ptr_offset + base_ptr;\n    v3 = bottom_data[ptr3];\n  }\n  scalar_t v4 = 0;\n  if (h_high <= height - 1 && w_high <= width - 1)\n  {\n    const int ptr4 = h_high_ptr_offset + w_high_ptr_offset + base_ptr;\n    v4 = bottom_data[ptr4];\n  }\n\n  const scalar_t w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw;\n\n  const scalar_t val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4);\n  return val;\n}\n\n\ntemplate <typename scalar_t>\n__device__ void ms_deform_attn_col2im_bilinear(const scalar_t* &bottom_data, \n                                                   const int &height, const int &width, const int &nheads, const int &channels,\n                                                   const scalar_t &h, const scalar_t &w, const int &m, const int &c,\n                                                   const scalar_t &top_grad,\n                                                   const scalar_t &attn_weight,\n                                                   scalar_t* &grad_value, \n                                                   scalar_t* grad_sampling_loc,\n                                                   scalar_t* grad_attn_weight)\n{\n  const int h_low = floor(h);\n  const int w_low = floor(w);\n  const int h_high = h_low + 1;\n  const int w_high = w_low + 1;\n\n  const scalar_t lh = h - h_low;\n  const scalar_t lw = w - w_low;\n  const scalar_t hh = 1 - lh, hw = 1 - lw;\n\n  const int w_stride = nheads * channels;\n  const int h_stride = width * w_stride;\n  const int h_low_ptr_offset = h_low * h_stride;\n  const int h_high_ptr_offset = h_low_ptr_offset + h_stride;\n  const int w_low_ptr_offset = w_low * w_stride;\n  const int w_high_ptr_offset = w_low_ptr_offset + w_stride;\n  const int base_ptr = m * channels + c;\n\n  const scalar_t w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw;\n  const scalar_t top_grad_value = top_grad * attn_weight;\n  scalar_t grad_h_weight = 0, grad_w_weight = 0;\n\n  scalar_t v1 = 0;\n  if (h_low >= 0 && w_low >= 0)\n  {\n    const int ptr1 = h_low_ptr_offset + w_low_ptr_offset + base_ptr;\n    v1 = bottom_data[ptr1];\n    grad_h_weight -= hw * v1;\n    grad_w_weight -= hh * v1;\n    atomicAdd(grad_value+ptr1, w1*top_grad_value);\n  }\n  scalar_t v2 = 0;\n  if (h_low >= 0 && w_high <= width - 1)\n  {\n    const int ptr2 = h_low_ptr_offset + w_high_ptr_offset + base_ptr;\n    v2 = bottom_data[ptr2];\n    grad_h_weight -= lw * v2;\n    grad_w_weight += hh * v2;\n    atomicAdd(grad_value+ptr2, w2*top_grad_value);\n  }\n  scalar_t v3 = 0;\n  if (h_high <= height - 1 && w_low >= 0)\n  {\n    const int ptr3 = h_high_ptr_offset + w_low_ptr_offset + base_ptr;\n    v3 = bottom_data[ptr3];\n    grad_h_weight += hw * v3;\n    grad_w_weight -= lh * v3;\n    atomicAdd(grad_value+ptr3, w3*top_grad_value); \n  }\n  scalar_t v4 = 0;\n  if (h_high <= height - 1 && w_high <= width - 1)\n  {\n    const int ptr4 = h_high_ptr_offset + w_high_ptr_offset + base_ptr;\n    v4 = bottom_data[ptr4];\n    grad_h_weight += lw * v4;\n    grad_w_weight += lh * v4;\n    atomicAdd(grad_value+ptr4, w4*top_grad_value);\n  }\n\n  const scalar_t val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4);\n  *grad_attn_weight = top_grad * val;\n  *grad_sampling_loc = width * grad_w_weight * top_grad_value;\n  *(grad_sampling_loc + 1) = height * grad_h_weight * top_grad_value;\n}\n\n\ntemplate <typename scalar_t>\n__device__ void ms_deform_attn_col2im_bilinear_gm(const scalar_t* &bottom_data, \n                                                   const int &height, const int &width, const int &nheads, const int &channels,\n                                                   const scalar_t &h, const scalar_t &w, const int &m, const int &c,\n                                                   const scalar_t &top_grad,\n                                                   const scalar_t &attn_weight,\n                                                   scalar_t* &grad_value, \n                                                   scalar_t* grad_sampling_loc,\n                                                   scalar_t* grad_attn_weight)\n{\n  const int h_low = floor(h);\n  const int w_low = floor(w);\n  const int h_high = h_low + 1;\n  const int w_high = w_low + 1;\n\n  const scalar_t lh = h - h_low;\n  const scalar_t lw = w - w_low;\n  const scalar_t hh = 1 - lh, hw = 1 - lw;\n\n  const int w_stride = nheads * channels;\n  const int h_stride = width * w_stride;\n  const int h_low_ptr_offset = h_low * h_stride;\n  const int h_high_ptr_offset = h_low_ptr_offset + h_stride;\n  const int w_low_ptr_offset = w_low * w_stride;\n  const int w_high_ptr_offset = w_low_ptr_offset + w_stride;\n  const int base_ptr = m * channels + c;\n\n  const scalar_t w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw;\n  const scalar_t top_grad_value = top_grad * attn_weight;\n  scalar_t grad_h_weight = 0, grad_w_weight = 0;\n\n  scalar_t v1 = 0;\n  if (h_low >= 0 && w_low >= 0)\n  {\n    const int ptr1 = h_low_ptr_offset + w_low_ptr_offset + base_ptr;\n    v1 = bottom_data[ptr1];\n    grad_h_weight -= hw * v1;\n    grad_w_weight -= hh * v1;\n    atomicAdd(grad_value+ptr1, w1*top_grad_value);\n  }\n  scalar_t v2 = 0;\n  if (h_low >= 0 && w_high <= width - 1)\n  {\n    const int ptr2 = h_low_ptr_offset + w_high_ptr_offset + base_ptr;\n    v2 = bottom_data[ptr2];\n    grad_h_weight -= lw * v2;\n    grad_w_weight += hh * v2;\n    atomicAdd(grad_value+ptr2, w2*top_grad_value);\n  }\n  scalar_t v3 = 0;\n  if (h_high <= height - 1 && w_low >= 0)\n  {\n    const int ptr3 = h_high_ptr_offset + w_low_ptr_offset + base_ptr;\n    v3 = bottom_data[ptr3];\n    grad_h_weight += hw * v3;\n    grad_w_weight -= lh * v3;\n    atomicAdd(grad_value+ptr3, w3*top_grad_value); \n  }\n  scalar_t v4 = 0;\n  if (h_high <= height - 1 && w_high <= width - 1)\n  {\n    const int ptr4 = h_high_ptr_offset + w_high_ptr_offset + base_ptr;\n    v4 = bottom_data[ptr4];\n    grad_h_weight += lw * v4;\n    grad_w_weight += lh * v4;\n    atomicAdd(grad_value+ptr4, w4*top_grad_value);\n  }\n\n  const scalar_t val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4);\n  atomicAdd(grad_attn_weight, top_grad * val); \n  atomicAdd(grad_sampling_loc, width * grad_w_weight * top_grad_value);\n  atomicAdd(grad_sampling_loc + 1, height * grad_h_weight * top_grad_value);\n}\n\n\ntemplate <typename scalar_t>\n__global__ void ms_deformable_im2col_gpu_kernel(const int n,\n                                                const scalar_t *data_value, \n                                                const int64_t *data_spatial_shapes,\n                                                const int64_t *data_level_start_index, \n                                                const scalar_t *data_sampling_loc,\n                                                const scalar_t *data_attn_weight,\n                                                const int batch_size, \n                                                const int spatial_size, \n                                                const int num_heads,\n                                                const int channels, \n                                                const int num_levels,\n                                                const int num_query,\n                                                const int num_point,\n                                                scalar_t *data_col)\n{\n  CUDA_KERNEL_LOOP(index, n)\n  {\n    int _temp = index;\n    const int c_col = _temp % channels;\n    _temp /= channels;\n    const int sampling_index = _temp; \n    const int m_col = _temp % num_heads;\n    _temp /= num_heads;\n    const int q_col = _temp % num_query;\n    _temp /= num_query;\n    const int b_col = _temp;\n\n    scalar_t *data_col_ptr = data_col + index;\n    int data_weight_ptr = sampling_index * num_levels * num_point;\n    int data_loc_w_ptr = data_weight_ptr << 1;\n    const int qid_stride = num_heads * channels;\n    const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride;\n    scalar_t col = 0;\n    \n    for (int l_col=0; l_col < num_levels; ++l_col)\n    {\n      const int level_start_id = data_level_start_index[l_col];\n      const int spatial_h_ptr = l_col << 1;\n      const int spatial_h = data_spatial_shapes[spatial_h_ptr];\n      const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1];\n      const scalar_t *data_value_ptr = data_value + (data_value_ptr_init_offset + level_start_id * qid_stride);\n      for (int p_col=0; p_col < num_point; ++p_col)\n      {\n        const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr];\n        const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1];\n        const scalar_t weight = data_attn_weight[data_weight_ptr];\n\n        const scalar_t h_im = loc_h * spatial_h - 0.5;\n        const scalar_t w_im = loc_w * spatial_w - 0.5;\n\n        if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w)\n        {\n          col += ms_deform_attn_im2col_bilinear(data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col) * weight;\n        }\n\n        data_weight_ptr += 1;\n        data_loc_w_ptr += 2;\n      }\n    }\n    *data_col_ptr = col;\n  }\n}\n\ntemplate <typename scalar_t, unsigned int blockSize>\n__global__ void ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1(const int n,\n                                                const scalar_t *grad_col,\n                                                const scalar_t *data_value,\n                                                const int64_t *data_spatial_shapes,\n                                                const int64_t *data_level_start_index, \n                                                const scalar_t *data_sampling_loc,\n                                                const scalar_t *data_attn_weight,\n                                                const int batch_size, \n                                                const int spatial_size, \n                                                const int num_heads,\n                                                const int channels, \n                                                const int num_levels,\n                                                const int num_query,\n                                                const int num_point,\n                                                scalar_t *grad_value,\n                                                scalar_t *grad_sampling_loc,\n                                                scalar_t *grad_attn_weight)\n{\n  CUDA_KERNEL_LOOP(index, n)\n  {\n    __shared__ scalar_t cache_grad_sampling_loc[blockSize * 2];\n    __shared__ scalar_t cache_grad_attn_weight[blockSize];\n    unsigned int tid = threadIdx.x;\n    int _temp = index;\n    const int c_col = _temp % channels;\n    _temp /= channels;\n    const int sampling_index = _temp; \n    const int m_col = _temp % num_heads;\n    _temp /= num_heads;\n    const int q_col = _temp % num_query;\n    _temp /= num_query;\n    const int b_col = _temp;\n\n    const scalar_t top_grad = grad_col[index];\n\n    int data_weight_ptr = sampling_index * num_levels * num_point;\n    int data_loc_w_ptr = data_weight_ptr << 1;\n    const int grad_sampling_ptr = data_weight_ptr;\n    grad_sampling_loc += grad_sampling_ptr << 1;\n    grad_attn_weight += grad_sampling_ptr;\n    const int grad_weight_stride = 1;\n    const int grad_loc_stride = 2;\n    const int qid_stride = num_heads * channels;\n    const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride;\n\n    for (int l_col=0; l_col < num_levels; ++l_col)\n    {\n      const int level_start_id = data_level_start_index[l_col];\n      const int spatial_h_ptr = l_col << 1;\n      const int spatial_h = data_spatial_shapes[spatial_h_ptr];\n      const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1];\n      const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride;\n      const scalar_t *data_value_ptr = data_value + value_ptr_offset;\n      scalar_t *grad_value_ptr = grad_value + value_ptr_offset;\n\n      for (int p_col=0; p_col < num_point; ++p_col)\n      {\n        const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr];\n        const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1];\n        const scalar_t weight = data_attn_weight[data_weight_ptr];\n\n        const scalar_t h_im = loc_h * spatial_h - 0.5;\n        const scalar_t w_im = loc_w * spatial_w - 0.5;\n        *(cache_grad_sampling_loc+(threadIdx.x << 1)) = 0;\n        *(cache_grad_sampling_loc+((threadIdx.x << 1) + 1)) = 0;\n        *(cache_grad_attn_weight+threadIdx.x)=0;\n        if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w)\n        {\n          ms_deform_attn_col2im_bilinear(\n            data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col,\n            top_grad, weight, grad_value_ptr, \n            cache_grad_sampling_loc+(threadIdx.x << 1), cache_grad_attn_weight+threadIdx.x);\n        }\n        \n        __syncthreads();\n        if (tid == 0)\n        {\n          scalar_t _grad_w=cache_grad_sampling_loc[0], _grad_h=cache_grad_sampling_loc[1], _grad_a=cache_grad_attn_weight[0];\n          int sid=2;\n          for (unsigned int tid = 1; tid < blockSize; ++tid)\n          {\n            _grad_w += cache_grad_sampling_loc[sid];\n            _grad_h += cache_grad_sampling_loc[sid + 1];\n            _grad_a += cache_grad_attn_weight[tid];\n            sid += 2;\n          }\n          \n          \n          *grad_sampling_loc = _grad_w;\n          *(grad_sampling_loc + 1) = _grad_h;\n          *grad_attn_weight = _grad_a;\n        }\n        __syncthreads();\n\n        data_weight_ptr += 1;\n        data_loc_w_ptr += 2;\n        grad_attn_weight += grad_weight_stride;\n        grad_sampling_loc += grad_loc_stride;\n      }\n    }\n  }\n}\n\n\ntemplate <typename scalar_t, unsigned int blockSize>\n__global__ void ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2(const int n,\n                                                const scalar_t *grad_col,\n                                                const scalar_t *data_value,\n                                                const int64_t *data_spatial_shapes,\n                                                const int64_t *data_level_start_index, \n                                                const scalar_t *data_sampling_loc,\n                                                const scalar_t *data_attn_weight,\n                                                const int batch_size, \n                                                const int spatial_size, \n                                                const int num_heads,\n                                                const int channels, \n                                                const int num_levels,\n                                                const int num_query,\n                                                const int num_point,\n                                                scalar_t *grad_value,\n                                                scalar_t *grad_sampling_loc,\n                                                scalar_t *grad_attn_weight)\n{\n  CUDA_KERNEL_LOOP(index, n)\n  {\n    __shared__ scalar_t cache_grad_sampling_loc[blockSize * 2];\n    __shared__ scalar_t cache_grad_attn_weight[blockSize];\n    unsigned int tid = threadIdx.x;\n    int _temp = index;\n    const int c_col = _temp % channels;\n    _temp /= channels;\n    const int sampling_index = _temp; \n    const int m_col = _temp % num_heads;\n    _temp /= num_heads;\n    const int q_col = _temp % num_query;\n    _temp /= num_query;\n    const int b_col = _temp;\n\n    const scalar_t top_grad = grad_col[index];\n\n    int data_weight_ptr = sampling_index * num_levels * num_point;\n    int data_loc_w_ptr = data_weight_ptr << 1;\n    const int grad_sampling_ptr = data_weight_ptr;\n    grad_sampling_loc += grad_sampling_ptr << 1;\n    grad_attn_weight += grad_sampling_ptr;\n    const int grad_weight_stride = 1;\n    const int grad_loc_stride = 2;\n    const int qid_stride = num_heads * channels;\n    const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride;\n\n    for (int l_col=0; l_col < num_levels; ++l_col)\n    {\n      const int level_start_id = data_level_start_index[l_col];\n      const int spatial_h_ptr = l_col << 1;\n      const int spatial_h = data_spatial_shapes[spatial_h_ptr];\n      const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1];\n      const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride;\n      const scalar_t *data_value_ptr = data_value + value_ptr_offset;\n      scalar_t *grad_value_ptr = grad_value + value_ptr_offset;\n\n      for (int p_col=0; p_col < num_point; ++p_col)\n      {\n        const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr];\n        const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1];\n        const scalar_t weight = data_attn_weight[data_weight_ptr];\n\n        const scalar_t h_im = loc_h * spatial_h - 0.5;\n        const scalar_t w_im = loc_w * spatial_w - 0.5;\n        *(cache_grad_sampling_loc+(threadIdx.x << 1)) = 0;\n        *(cache_grad_sampling_loc+((threadIdx.x << 1) + 1)) = 0;\n        *(cache_grad_attn_weight+threadIdx.x)=0;\n        if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w)\n        {\n          ms_deform_attn_col2im_bilinear(\n            data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col,\n            top_grad, weight, grad_value_ptr, \n            cache_grad_sampling_loc+(threadIdx.x << 1), cache_grad_attn_weight+threadIdx.x);\n        }\n        \n        __syncthreads();\n\n        for (unsigned int s=blockSize/2; s>0; s>>=1)\n        {\n          if (tid < s) {\n            const unsigned int xid1 = tid << 1;\n            const unsigned int xid2 = (tid + s) << 1;\n            cache_grad_attn_weight[tid] += cache_grad_attn_weight[tid + s];\n            cache_grad_sampling_loc[xid1] += cache_grad_sampling_loc[xid2];\n            cache_grad_sampling_loc[xid1 + 1] += cache_grad_sampling_loc[xid2 + 1];\n          }\n          __syncthreads();\n        }\n\n        if (tid == 0)\n        { \n          *grad_sampling_loc = cache_grad_sampling_loc[0];\n          *(grad_sampling_loc + 1) = cache_grad_sampling_loc[1];\n          *grad_attn_weight = cache_grad_attn_weight[0];\n        }\n        __syncthreads();\n\n        data_weight_ptr += 1;\n        data_loc_w_ptr += 2;\n        grad_attn_weight += grad_weight_stride;\n        grad_sampling_loc += grad_loc_stride;\n      }\n    }\n  }\n}\n\n\ntemplate <typename scalar_t>\n__global__ void ms_deformable_col2im_gpu_kernel_shm_reduce_v1(const int n,\n                                                const scalar_t *grad_col,\n                                                const scalar_t *data_value,\n                                                const int64_t *data_spatial_shapes,\n                                                const int64_t *data_level_start_index, \n                                                const scalar_t *data_sampling_loc,\n                                                const scalar_t *data_attn_weight,\n                                                const int batch_size, \n                                                const int spatial_size, \n                                                const int num_heads,\n                                                const int channels, \n                                                const int num_levels,\n                                                const int num_query,\n                                                const int num_point,\n                                                scalar_t *grad_value,\n                                                scalar_t *grad_sampling_loc,\n                                                scalar_t *grad_attn_weight)\n{\n  CUDA_KERNEL_LOOP(index, n)\n  {\n    extern __shared__ int _s[];\n    scalar_t* cache_grad_sampling_loc = (scalar_t*)_s;\n    scalar_t* cache_grad_attn_weight = cache_grad_sampling_loc + 2 * blockDim.x;\n    unsigned int tid = threadIdx.x;\n    int _temp = index;\n    const int c_col = _temp % channels;\n    _temp /= channels;\n    const int sampling_index = _temp; \n    const int m_col = _temp % num_heads;\n    _temp /= num_heads;\n    const int q_col = _temp % num_query;\n    _temp /= num_query;\n    const int b_col = _temp;\n\n    const scalar_t top_grad = grad_col[index];\n\n    int data_weight_ptr = sampling_index * num_levels * num_point;\n    int data_loc_w_ptr = data_weight_ptr << 1;\n    const int grad_sampling_ptr = data_weight_ptr;\n    grad_sampling_loc += grad_sampling_ptr << 1;\n    grad_attn_weight += grad_sampling_ptr;\n    const int grad_weight_stride = 1;\n    const int grad_loc_stride = 2;\n    const int qid_stride = num_heads * channels;\n    const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride;\n\n    for (int l_col=0; l_col < num_levels; ++l_col)\n    {\n      const int level_start_id = data_level_start_index[l_col];\n      const int spatial_h_ptr = l_col << 1;\n      const int spatial_h = data_spatial_shapes[spatial_h_ptr];\n      const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1];\n      const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride;\n      const scalar_t *data_value_ptr = data_value + value_ptr_offset;\n      scalar_t *grad_value_ptr = grad_value + value_ptr_offset;\n\n      for (int p_col=0; p_col < num_point; ++p_col)\n      {\n        const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr];\n        const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1];\n        const scalar_t weight = data_attn_weight[data_weight_ptr];\n\n        const scalar_t h_im = loc_h * spatial_h - 0.5;\n        const scalar_t w_im = loc_w * spatial_w - 0.5;\n        *(cache_grad_sampling_loc+(threadIdx.x << 1)) = 0;\n        *(cache_grad_sampling_loc+((threadIdx.x << 1) + 1)) = 0;\n        *(cache_grad_attn_weight+threadIdx.x)=0;\n        if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w)\n        {\n          ms_deform_attn_col2im_bilinear(\n            data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col,\n            top_grad, weight, grad_value_ptr, \n            cache_grad_sampling_loc+(threadIdx.x << 1), cache_grad_attn_weight+threadIdx.x);\n        }\n        \n        __syncthreads();\n        if (tid == 0)\n        {\n          scalar_t _grad_w=cache_grad_sampling_loc[0], _grad_h=cache_grad_sampling_loc[1], _grad_a=cache_grad_attn_weight[0];\n          int sid=2;\n          for (unsigned int tid = 1; tid < blockDim.x; ++tid)\n          {\n            _grad_w += cache_grad_sampling_loc[sid];\n            _grad_h += cache_grad_sampling_loc[sid + 1];\n            _grad_a += cache_grad_attn_weight[tid];\n            sid += 2;\n          }\n          \n          \n          *grad_sampling_loc = _grad_w;\n          *(grad_sampling_loc + 1) = _grad_h;\n          *grad_attn_weight = _grad_a;\n        }\n        __syncthreads();\n\n        data_weight_ptr += 1;\n        data_loc_w_ptr += 2;\n        grad_attn_weight += grad_weight_stride;\n        grad_sampling_loc += grad_loc_stride;\n      }\n    }\n  }\n}\n\ntemplate <typename scalar_t>\n__global__ void ms_deformable_col2im_gpu_kernel_shm_reduce_v2(const int n,\n                                                const scalar_t *grad_col,\n                                                const scalar_t *data_value,\n                                                const int64_t *data_spatial_shapes,\n                                                const int64_t *data_level_start_index, \n                                                const scalar_t *data_sampling_loc,\n                                                const scalar_t *data_attn_weight,\n                                                const int batch_size, \n                                                const int spatial_size, \n                                                const int num_heads,\n                                                const int channels, \n                                                const int num_levels,\n                                                const int num_query,\n                                                const int num_point,\n                                                scalar_t *grad_value,\n                                                scalar_t *grad_sampling_loc,\n                                                scalar_t *grad_attn_weight)\n{\n  CUDA_KERNEL_LOOP(index, n)\n  {\n    extern __shared__ int _s[];\n    scalar_t* cache_grad_sampling_loc = (scalar_t*)_s;\n    scalar_t* cache_grad_attn_weight = cache_grad_sampling_loc + 2 * blockDim.x;\n    unsigned int tid = threadIdx.x;\n    int _temp = index;\n    const int c_col = _temp % channels;\n    _temp /= channels;\n    const int sampling_index = _temp; \n    const int m_col = _temp % num_heads;\n    _temp /= num_heads;\n    const int q_col = _temp % num_query;\n    _temp /= num_query;\n    const int b_col = _temp;\n\n    const scalar_t top_grad = grad_col[index];\n\n    int data_weight_ptr = sampling_index * num_levels * num_point;\n    int data_loc_w_ptr = data_weight_ptr << 1;\n    const int grad_sampling_ptr = data_weight_ptr;\n    grad_sampling_loc += grad_sampling_ptr << 1;\n    grad_attn_weight += grad_sampling_ptr;\n    const int grad_weight_stride = 1;\n    const int grad_loc_stride = 2;\n    const int qid_stride = num_heads * channels;\n    const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride;\n\n    for (int l_col=0; l_col < num_levels; ++l_col)\n    {\n      const int level_start_id = data_level_start_index[l_col];\n      const int spatial_h_ptr = l_col << 1;\n      const int spatial_h = data_spatial_shapes[spatial_h_ptr];\n      const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1];\n      const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride;\n      const scalar_t *data_value_ptr = data_value + value_ptr_offset;\n      scalar_t *grad_value_ptr = grad_value + value_ptr_offset;\n\n      for (int p_col=0; p_col < num_point; ++p_col)\n      {\n        const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr];\n        const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1];\n        const scalar_t weight = data_attn_weight[data_weight_ptr];\n\n        const scalar_t h_im = loc_h * spatial_h - 0.5;\n        const scalar_t w_im = loc_w * spatial_w - 0.5;\n        *(cache_grad_sampling_loc+(threadIdx.x << 1)) = 0;\n        *(cache_grad_sampling_loc+((threadIdx.x << 1) + 1)) = 0;\n        *(cache_grad_attn_weight+threadIdx.x)=0;\n        if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w)\n        {\n          ms_deform_attn_col2im_bilinear(\n            data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col,\n            top_grad, weight, grad_value_ptr, \n            cache_grad_sampling_loc+(threadIdx.x << 1), cache_grad_attn_weight+threadIdx.x);\n        }\n        \n        __syncthreads();\n\n        for (unsigned int s=blockDim.x/2, spre=blockDim.x; s>0; s>>=1, spre>>=1)\n        {\n          if (tid < s) {\n            const unsigned int xid1 = tid << 1;\n            const unsigned int xid2 = (tid + s) << 1;\n            cache_grad_attn_weight[tid] += cache_grad_attn_weight[tid + s];\n            cache_grad_sampling_loc[xid1] += cache_grad_sampling_loc[xid2];\n            cache_grad_sampling_loc[xid1 + 1] += cache_grad_sampling_loc[xid2 + 1];\n            if (tid + (s << 1) < spre)\n            {\n              cache_grad_attn_weight[tid] += cache_grad_attn_weight[tid + (s << 1)];\n              cache_grad_sampling_loc[xid1] += cache_grad_sampling_loc[xid2 + (s << 1)];\n              cache_grad_sampling_loc[xid1 + 1] += cache_grad_sampling_loc[xid2 + 1 + (s << 1)];\n            } \n          }\n          __syncthreads();\n        }\n\n        if (tid == 0)\n        {\n          *grad_sampling_loc = cache_grad_sampling_loc[0];\n          *(grad_sampling_loc + 1) = cache_grad_sampling_loc[1];\n          *grad_attn_weight = cache_grad_attn_weight[0];\n        }\n        __syncthreads();\n\n        data_weight_ptr += 1;\n        data_loc_w_ptr += 2;\n        grad_attn_weight += grad_weight_stride;\n        grad_sampling_loc += grad_loc_stride;\n      }\n    }\n  }\n}\n\ntemplate <typename scalar_t>\n__global__ void ms_deformable_col2im_gpu_kernel_shm_reduce_v2_multi_blocks(const int n,\n                                                const scalar_t *grad_col,\n                                                const scalar_t *data_value,\n                                                const int64_t *data_spatial_shapes,\n                                                const int64_t *data_level_start_index, \n                                                const scalar_t *data_sampling_loc,\n                                                const scalar_t *data_attn_weight,\n                                                const int batch_size, \n                                                const int spatial_size, \n                                                const int num_heads,\n                                                const int channels, \n                                                const int num_levels,\n                                                const int num_query,\n                                                const int num_point,\n                                                scalar_t *grad_value,\n                                                scalar_t *grad_sampling_loc,\n                                                scalar_t *grad_attn_weight)\n{\n  CUDA_KERNEL_LOOP(index, n)\n  {\n    extern __shared__ int _s[];\n    scalar_t* cache_grad_sampling_loc = (scalar_t*)_s;\n    scalar_t* cache_grad_attn_weight = cache_grad_sampling_loc + 2 * blockDim.x;\n    unsigned int tid = threadIdx.x;\n    int _temp = index;\n    const int c_col = _temp % channels;\n    _temp /= channels;\n    const int sampling_index = _temp; \n    const int m_col = _temp % num_heads;\n    _temp /= num_heads;\n    const int q_col = _temp % num_query;\n    _temp /= num_query;\n    const int b_col = _temp;\n\n    const scalar_t top_grad = grad_col[index];\n\n    int data_weight_ptr = sampling_index * num_levels * num_point;\n    int data_loc_w_ptr = data_weight_ptr << 1;\n    const int grad_sampling_ptr = data_weight_ptr;\n    grad_sampling_loc += grad_sampling_ptr << 1;\n    grad_attn_weight += grad_sampling_ptr;\n    const int grad_weight_stride = 1;\n    const int grad_loc_stride = 2;\n    const int qid_stride = num_heads * channels;\n    const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride;\n\n    for (int l_col=0; l_col < num_levels; ++l_col)\n    {\n      const int level_start_id = data_level_start_index[l_col];\n      const int spatial_h_ptr = l_col << 1;\n      const int spatial_h = data_spatial_shapes[spatial_h_ptr];\n      const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1];\n      const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride;\n      const scalar_t *data_value_ptr = data_value + value_ptr_offset;\n      scalar_t *grad_value_ptr = grad_value + value_ptr_offset;\n\n      for (int p_col=0; p_col < num_point; ++p_col)\n      {\n        const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr];\n        const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1];\n        const scalar_t weight = data_attn_weight[data_weight_ptr];\n\n        const scalar_t h_im = loc_h * spatial_h - 0.5;\n        const scalar_t w_im = loc_w * spatial_w - 0.5;\n        *(cache_grad_sampling_loc+(threadIdx.x << 1)) = 0;\n        *(cache_grad_sampling_loc+((threadIdx.x << 1) + 1)) = 0;\n        *(cache_grad_attn_weight+threadIdx.x)=0;\n        if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w)\n        {\n          ms_deform_attn_col2im_bilinear(\n            data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col,\n            top_grad, weight, grad_value_ptr, \n            cache_grad_sampling_loc+(threadIdx.x << 1), cache_grad_attn_weight+threadIdx.x);\n        }\n        \n        __syncthreads();\n\n        for (unsigned int s=blockDim.x/2, spre=blockDim.x; s>0; s>>=1, spre>>=1)\n        {\n          if (tid < s) {\n            const unsigned int xid1 = tid << 1;\n            const unsigned int xid2 = (tid + s) << 1;\n            cache_grad_attn_weight[tid] += cache_grad_attn_weight[tid + s];\n            cache_grad_sampling_loc[xid1] += cache_grad_sampling_loc[xid2];\n            cache_grad_sampling_loc[xid1 + 1] += cache_grad_sampling_loc[xid2 + 1];\n            if (tid + (s << 1) < spre)\n            {\n              cache_grad_attn_weight[tid] += cache_grad_attn_weight[tid + (s << 1)];\n              cache_grad_sampling_loc[xid1] += cache_grad_sampling_loc[xid2 + (s << 1)];\n              cache_grad_sampling_loc[xid1 + 1] += cache_grad_sampling_loc[xid2 + 1 + (s << 1)];\n            }\n          }\n          __syncthreads();\n        }\n\n        if (tid == 0)\n        {\n          atomicAdd(grad_sampling_loc, cache_grad_sampling_loc[0]);\n          atomicAdd(grad_sampling_loc + 1, cache_grad_sampling_loc[1]);\n          atomicAdd(grad_attn_weight, cache_grad_attn_weight[0]);\n        }\n        __syncthreads();\n\n        data_weight_ptr += 1;\n        data_loc_w_ptr += 2;\n        grad_attn_weight += grad_weight_stride;\n        grad_sampling_loc += grad_loc_stride;\n      }\n    }\n  }\n}\n\n\ntemplate <typename scalar_t>\n__global__ void ms_deformable_col2im_gpu_kernel_gm(const int n,\n                                                const scalar_t *grad_col,\n                                                const scalar_t *data_value,\n                                                const int64_t *data_spatial_shapes,\n                                                const int64_t *data_level_start_index, \n                                                const scalar_t *data_sampling_loc,\n                                                const scalar_t *data_attn_weight,\n                                                const int batch_size, \n                                                const int spatial_size, \n                                                const int num_heads,\n                                                const int channels, \n                                                const int num_levels,\n                                                const int num_query,\n                                                const int num_point,\n                                                scalar_t *grad_value,\n                                                scalar_t *grad_sampling_loc,\n                                                scalar_t *grad_attn_weight)\n{\n  CUDA_KERNEL_LOOP(index, n)\n  {\n    int _temp = index;\n    const int c_col = _temp % channels;\n    _temp /= channels;\n    const int sampling_index = _temp; \n    const int m_col = _temp % num_heads;\n    _temp /= num_heads;\n    const int q_col = _temp % num_query;\n    _temp /= num_query;\n    const int b_col = _temp;\n\n    const scalar_t top_grad = grad_col[index];\n\n    int data_weight_ptr = sampling_index * num_levels * num_point;\n    int data_loc_w_ptr = data_weight_ptr << 1;\n    const int grad_sampling_ptr = data_weight_ptr;\n    grad_sampling_loc += grad_sampling_ptr << 1;\n    grad_attn_weight += grad_sampling_ptr;\n    const int grad_weight_stride = 1;\n    const int grad_loc_stride = 2;\n    const int qid_stride = num_heads * channels;\n    const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride;\n\n    for (int l_col=0; l_col < num_levels; ++l_col)\n    {\n      const int level_start_id = data_level_start_index[l_col];\n      const int spatial_h_ptr = l_col << 1;\n      const int spatial_h = data_spatial_shapes[spatial_h_ptr];\n      const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1];\n      const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride;\n      const scalar_t *data_value_ptr = data_value + value_ptr_offset;\n      scalar_t *grad_value_ptr = grad_value + value_ptr_offset;\n\n      for (int p_col=0; p_col < num_point; ++p_col)\n      {\n        const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr];\n        const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1];\n        const scalar_t weight = data_attn_weight[data_weight_ptr];\n\n        const scalar_t h_im = loc_h * spatial_h - 0.5;\n        const scalar_t w_im = loc_w * spatial_w - 0.5;\n        if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w)\n        {\n          ms_deform_attn_col2im_bilinear_gm(\n            data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col,\n            top_grad, weight, grad_value_ptr, \n            grad_sampling_loc, grad_attn_weight);\n        }\n        data_weight_ptr += 1;\n        data_loc_w_ptr += 2;\n        grad_attn_weight += grad_weight_stride;\n        grad_sampling_loc += grad_loc_stride;\n      }\n    }\n  }\n}\n\n\ntemplate <typename scalar_t>\nvoid ms_deformable_im2col_cuda(cudaStream_t stream,\n                              const scalar_t* data_value,\n                              const int64_t* data_spatial_shapes, \n                              const int64_t* data_level_start_index, \n                              const scalar_t* data_sampling_loc,\n                              const scalar_t* data_attn_weight,\n                              const int batch_size,\n                              const int spatial_size, \n                              const int num_heads, \n                              const int channels, \n                              const int num_levels, \n                              const int num_query,\n                              const int num_point,\n                              scalar_t* data_col)\n{\n  const int num_kernels = batch_size * num_query * num_heads * channels;\n  const int num_actual_kernels = batch_size * num_query * num_heads * channels;\n  const int num_threads = CUDA_NUM_THREADS;\n  ms_deformable_im2col_gpu_kernel<scalar_t>\n      <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n          0, stream>>>(\n      num_kernels, data_value, data_spatial_shapes, data_level_start_index, data_sampling_loc, data_attn_weight, \n      batch_size, spatial_size, num_heads, channels, num_levels, num_query, num_point, data_col);\n  \n  cudaError_t err = cudaGetLastError();\n  if (err != cudaSuccess)\n  {\n    printf(\"error in ms_deformable_im2col_cuda: %s\\n\", cudaGetErrorString(err));\n  }\n\n}\n\ntemplate <typename scalar_t>\nvoid ms_deformable_col2im_cuda(cudaStream_t stream,\n                              const scalar_t* grad_col,\n                              const scalar_t* data_value,\n                              const int64_t * data_spatial_shapes,\n                              const int64_t * data_level_start_index,\n                              const scalar_t * data_sampling_loc,\n                              const scalar_t * data_attn_weight,\n                              const int batch_size, \n                              const int spatial_size, \n                              const int num_heads,\n                              const int channels, \n                              const int num_levels,\n                              const int num_query,\n                              const int num_point, \n                              scalar_t* grad_value,\n                              scalar_t* grad_sampling_loc,\n                              scalar_t* grad_attn_weight)\n{\n  const int num_threads = (channels > CUDA_NUM_THREADS)?CUDA_NUM_THREADS:channels;\n  const int num_kernels = batch_size * num_query * num_heads * channels;\n  const int num_actual_kernels = batch_size * num_query * num_heads * channels;\n  if (channels > 1024)\n  {\n    if ((channels & 1023) == 0)\n    {\n      ms_deformable_col2im_gpu_kernel_shm_reduce_v2_multi_blocks<scalar_t>\n          <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n              num_threads*3*sizeof(scalar_t), stream>>>(\n                        num_kernels, \n                        grad_col,\n                        data_value,\n                        data_spatial_shapes,\n                        data_level_start_index, \n                        data_sampling_loc,\n                        data_attn_weight,\n                        batch_size, \n                        spatial_size, \n                        num_heads,\n                        channels, \n                        num_levels,\n                        num_query,\n                        num_point,\n                        grad_value,\n                        grad_sampling_loc,\n                        grad_attn_weight);\n    }\n    else\n    {\n      ms_deformable_col2im_gpu_kernel_gm<scalar_t>\n        <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n            0, stream>>>(\n                      num_kernels, \n                      grad_col,\n                      data_value,\n                      data_spatial_shapes,\n                      data_level_start_index, \n                      data_sampling_loc,\n                      data_attn_weight,\n                      batch_size, \n                      spatial_size, \n                      num_heads,\n                      channels, \n                      num_levels,\n                      num_query,\n                      num_point,\n                      grad_value,\n                      grad_sampling_loc,\n                      grad_attn_weight);\n    }\n  }\n  else{\n    switch(channels)\n    {\n      case 1:\n        ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1<scalar_t, 1>\n        <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n            0, stream>>>(\n                      num_kernels, \n                      grad_col,\n                      data_value,\n                      data_spatial_shapes,\n                      data_level_start_index, \n                      data_sampling_loc,\n                      data_attn_weight,\n                      batch_size, \n                      spatial_size, \n                      num_heads,\n                      channels, \n                      num_levels,\n                      num_query,\n                      num_point,\n                      grad_value,\n                      grad_sampling_loc,\n                      grad_attn_weight);\n        break;\n      case 2:\n        ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1<scalar_t, 2>\n        <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n            0, stream>>>(\n                      num_kernels, \n                      grad_col,\n                      data_value,\n                      data_spatial_shapes,\n                      data_level_start_index, \n                      data_sampling_loc,\n                      data_attn_weight,\n                      batch_size, \n                      spatial_size, \n                      num_heads,\n                      channels, \n                      num_levels,\n                      num_query,\n                      num_point,\n                      grad_value,\n                      grad_sampling_loc,\n                      grad_attn_weight);\n        break;\n      case 4:\n        ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1<scalar_t, 4>\n        <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n            0, stream>>>(\n                      num_kernels, \n                      grad_col,\n                      data_value,\n                      data_spatial_shapes,\n                      data_level_start_index, \n                      data_sampling_loc,\n                      data_attn_weight,\n                      batch_size, \n                      spatial_size, \n                      num_heads,\n                      channels, \n                      num_levels,\n                      num_query,\n                      num_point,\n                      grad_value,\n                      grad_sampling_loc,\n                      grad_attn_weight);\n        break;\n      case 8:\n        ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1<scalar_t, 8>\n        <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n            0, stream>>>(\n                      num_kernels, \n                      grad_col,\n                      data_value,\n                      data_spatial_shapes,\n                      data_level_start_index, \n                      data_sampling_loc,\n                      data_attn_weight,\n                      batch_size, \n                      spatial_size, \n                      num_heads,\n                      channels, \n                      num_levels,\n                      num_query,\n                      num_point,\n                      grad_value,\n                      grad_sampling_loc,\n                      grad_attn_weight);\n        break;\n      case 16:\n        ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1<scalar_t, 16>\n        <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n            0, stream>>>(\n                      num_kernels, \n                      grad_col,\n                      data_value,\n                      data_spatial_shapes,\n                      data_level_start_index, \n                      data_sampling_loc,\n                      data_attn_weight,\n                      batch_size, \n                      spatial_size, \n                      num_heads,\n                      channels, \n                      num_levels,\n                      num_query,\n                      num_point,\n                      grad_value,\n                      grad_sampling_loc,\n                      grad_attn_weight);\n        break;\n      case 32:\n        ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1<scalar_t, 32>\n        <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n            0, stream>>>(\n                      num_kernels, \n                      grad_col,\n                      data_value,\n                      data_spatial_shapes,\n                      data_level_start_index, \n                      data_sampling_loc,\n                      data_attn_weight,\n                      batch_size, \n                      spatial_size, \n                      num_heads,\n                      channels, \n                      num_levels,\n                      num_query,\n                      num_point,\n                      grad_value,\n                      grad_sampling_loc,\n                      grad_attn_weight);\n        break;\n      case 64:\n        ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2<scalar_t, 64>\n        <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n            0, stream>>>(\n                      num_kernels, \n                      grad_col,\n                      data_value,\n                      data_spatial_shapes,\n                      data_level_start_index, \n                      data_sampling_loc,\n                      data_attn_weight,\n                      batch_size, \n                      spatial_size, \n                      num_heads,\n                      channels, \n                      num_levels,\n                      num_query,\n                      num_point,\n                      grad_value,\n                      grad_sampling_loc,\n                      grad_attn_weight);\n        break;\n      case 128:\n        ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2<scalar_t, 128>\n        <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n            0, stream>>>(\n                      num_kernels, \n                      grad_col,\n                      data_value,\n                      data_spatial_shapes,\n                      data_level_start_index, \n                      data_sampling_loc,\n                      data_attn_weight,\n                      batch_size, \n                      spatial_size, \n                      num_heads,\n                      channels, \n                      num_levels,\n                      num_query,\n                      num_point,\n                      grad_value,\n                      grad_sampling_loc,\n                      grad_attn_weight);\n        break;\n      case 256:\n        ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2<scalar_t, 256>\n        <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n            0, stream>>>(\n                      num_kernels, \n                      grad_col,\n                      data_value,\n                      data_spatial_shapes,\n                      data_level_start_index, \n                      data_sampling_loc,\n                      data_attn_weight,\n                      batch_size, \n                      spatial_size, \n                      num_heads,\n                      channels, \n                      num_levels,\n                      num_query,\n                      num_point,\n                      grad_value,\n                      grad_sampling_loc,\n                      grad_attn_weight);\n        break;\n      case 512:\n        ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2<scalar_t, 512>\n        <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n            0, stream>>>(\n                      num_kernels, \n                      grad_col,\n                      data_value,\n                      data_spatial_shapes,\n                      data_level_start_index, \n                      data_sampling_loc,\n                      data_attn_weight,\n                      batch_size, \n                      spatial_size, \n                      num_heads,\n                      channels, \n                      num_levels,\n                      num_query,\n                      num_point,\n                      grad_value,\n                      grad_sampling_loc,\n                      grad_attn_weight);\n        break;\n      case 1024:\n        ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2<scalar_t, 1024>\n        <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n            0, stream>>>(\n                      num_kernels, \n                      grad_col,\n                      data_value,\n                      data_spatial_shapes,\n                      data_level_start_index, \n                      data_sampling_loc,\n                      data_attn_weight,\n                      batch_size, \n                      spatial_size, \n                      num_heads,\n                      channels, \n                      num_levels,\n                      num_query,\n                      num_point,\n                      grad_value,\n                      grad_sampling_loc,\n                      grad_attn_weight);\n        break;\n      default:\n        if (channels < 64)\n        {\n          ms_deformable_col2im_gpu_kernel_shm_reduce_v1<scalar_t>\n          <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n              num_threads*3*sizeof(scalar_t), stream>>>(\n                        num_kernels, \n                        grad_col,\n                        data_value,\n                        data_spatial_shapes,\n                        data_level_start_index, \n                        data_sampling_loc,\n                        data_attn_weight,\n                        batch_size, \n                        spatial_size, \n                        num_heads,\n                        channels, \n                        num_levels,\n                        num_query,\n                        num_point,\n                        grad_value,\n                        grad_sampling_loc,\n                        grad_attn_weight);\n        }\n        else\n        {\n          ms_deformable_col2im_gpu_kernel_shm_reduce_v2<scalar_t>\n          <<<GET_BLOCKS(num_actual_kernels, num_threads), num_threads,\n              num_threads*3*sizeof(scalar_t), stream>>>(\n                        num_kernels, \n                        grad_col,\n                        data_value,\n                        data_spatial_shapes,\n                        data_level_start_index, \n                        data_sampling_loc,\n                        data_attn_weight,\n                        batch_size, \n                        spatial_size, \n                        num_heads,\n                        channels, \n                        num_levels,\n                        num_query,\n                        num_point,\n                        grad_value,\n                        grad_sampling_loc,\n                        grad_attn_weight);\n        }\n    }\n  }\n  cudaError_t err = cudaGetLastError();\n  if (err != cudaSuccess)\n  {\n    printf(\"error in ms_deformable_col2im_cuda: %s\\n\", cudaGetErrorString(err));\n  }\n\n}"
  },
  {
    "path": "models/encoder/ops/src/ms_deform_attn.h",
    "content": "/*!\n**************************************************************************************************\n* Deformable DETR\n* Copyright (c) 2020 SenseTime. All Rights Reserved.\n* Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n**************************************************************************************************\n* Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n**************************************************************************************************\n*/\n\n#pragma once\n\n#include \"cpu/ms_deform_attn_cpu.h\"\n\n#ifdef WITH_CUDA\n#include \"cuda/ms_deform_attn_cuda.h\"\n#endif\n\n\nat::Tensor\nms_deform_attn_forward(\n    const at::Tensor &value, \n    const at::Tensor &spatial_shapes,\n    const at::Tensor &level_start_index,\n    const at::Tensor &sampling_loc,\n    const at::Tensor &attn_weight,\n    const int im2col_step)\n{\n    if (value.type().is_cuda())\n    {\n#ifdef WITH_CUDA\n        return ms_deform_attn_cuda_forward(\n            value, spatial_shapes, level_start_index, sampling_loc, attn_weight, im2col_step);\n#else\n        AT_ERROR(\"Not compiled with GPU support\");\n#endif\n    }\n    AT_ERROR(\"Not implemented on the CPU\");\n}\n\nstd::vector<at::Tensor>\nms_deform_attn_backward(\n    const at::Tensor &value, \n    const at::Tensor &spatial_shapes,\n    const at::Tensor &level_start_index,\n    const at::Tensor &sampling_loc,\n    const at::Tensor &attn_weight,\n    const at::Tensor &grad_output,\n    const int im2col_step)\n{\n    if (value.type().is_cuda())\n    {\n#ifdef WITH_CUDA\n        return ms_deform_attn_cuda_backward(\n            value, spatial_shapes, level_start_index, sampling_loc, attn_weight, grad_output, im2col_step);\n#else\n        AT_ERROR(\"Not compiled with GPU support\");\n#endif\n    }\n    AT_ERROR(\"Not implemented on the CPU\");\n}\n\n"
  },
  {
    "path": "models/encoder/ops/src/vision.cpp",
    "content": "/*!\n**************************************************************************************************\n* Deformable DETR\n* Copyright (c) 2020 SenseTime. All Rights Reserved.\n* Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n**************************************************************************************************\n* Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n**************************************************************************************************\n*/\n\n#include \"ms_deform_attn.h\"\n\nPYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {\n  m.def(\"ms_deform_attn_forward\", &ms_deform_attn_forward, \"ms_deform_attn_forward\");\n  m.def(\"ms_deform_attn_backward\", &ms_deform_attn_backward, \"ms_deform_attn_backward\");\n}\n"
  },
  {
    "path": "models/encoder/ops/test.py",
    "content": "# ------------------------------------------------------------------------------------------------\n# Deformable DETR\n# Copyright (c) 2020 SenseTime. All Rights Reserved.\n# Licensed under the Apache License, Version 2.0 [see LICENSE for details]\n# ------------------------------------------------------------------------------------------------\n# Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0\n# ------------------------------------------------------------------------------------------------\n\nfrom __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import division\n\nimport time\nimport torch\nimport torch.nn as nn\nfrom torch.autograd import gradcheck\n\nfrom functions.ms_deform_attn_func import MSDeformAttnFunction, ms_deform_attn_core_pytorch\n\n\nN, M, D = 1, 2, 2\nLq, L, P = 2, 2, 2\nshapes = torch.as_tensor([(6, 4), (3, 2)], dtype=torch.long).cuda()\nlevel_start_index = torch.cat((shapes.new_zeros((1, )), shapes.prod(1).cumsum(0)[:-1]))\nS = sum([(H*W).item() for H, W in shapes])\n\n\ntorch.manual_seed(3)\n\n\n@torch.no_grad()\ndef check_forward_equal_with_pytorch_double():\n    value = torch.rand(N, S, M, D).cuda() * 0.01\n    sampling_locations = torch.rand(N, Lq, M, L, P, 2).cuda()\n    attention_weights = torch.rand(N, Lq, M, L, P).cuda() + 1e-5\n    attention_weights /= attention_weights.sum(-1, keepdim=True).sum(-2, keepdim=True)\n    im2col_step = 2\n    output_pytorch = ms_deform_attn_core_pytorch(value.double(), shapes, sampling_locations.double(), attention_weights.double()).detach().cpu()\n    output_cuda = MSDeformAttnFunction.apply(value.double(), shapes, level_start_index, sampling_locations.double(), attention_weights.double(), im2col_step).detach().cpu()\n    fwdok = torch.allclose(output_cuda, output_pytorch)\n    max_abs_err = (output_cuda - output_pytorch).abs().max()\n    max_rel_err = ((output_cuda - output_pytorch).abs() / output_pytorch.abs()).max()\n\n    print(f'* {fwdok} check_forward_equal_with_pytorch_double: max_abs_err {max_abs_err:.2e} max_rel_err {max_rel_err:.2e}')\n\n\n@torch.no_grad()\ndef check_forward_equal_with_pytorch_float():\n    value = torch.rand(N, S, M, D).cuda() * 0.01\n    sampling_locations = torch.rand(N, Lq, M, L, P, 2).cuda()\n    attention_weights = torch.rand(N, Lq, M, L, P).cuda() + 1e-5\n    attention_weights /= attention_weights.sum(-1, keepdim=True).sum(-2, keepdim=True)\n    im2col_step = 2\n    output_pytorch = ms_deform_attn_core_pytorch(value, shapes, sampling_locations, attention_weights).detach().cpu()\n    output_cuda = MSDeformAttnFunction.apply(value, shapes, level_start_index, sampling_locations, attention_weights, im2col_step).detach().cpu()\n    fwdok = torch.allclose(output_cuda, output_pytorch, rtol=1e-2, atol=1e-3)\n    max_abs_err = (output_cuda - output_pytorch).abs().max()\n    max_rel_err = ((output_cuda - output_pytorch).abs() / output_pytorch.abs()).max()\n\n    print(f'* {fwdok} check_forward_equal_with_pytorch_float: max_abs_err {max_abs_err:.2e} max_rel_err {max_rel_err:.2e}')\n\n\ndef check_gradient_numerical(channels=4, grad_value=True, grad_sampling_loc=True, grad_attn_weight=True):\n\n    value = torch.rand(N, S, M, channels).cuda() * 0.01\n    sampling_locations = torch.rand(N, Lq, M, L, P, 2).cuda()\n    attention_weights = torch.rand(N, Lq, M, L, P).cuda() + 1e-5\n    attention_weights /= attention_weights.sum(-1, keepdim=True).sum(-2, keepdim=True)\n    im2col_step = 2\n    func = MSDeformAttnFunction.apply\n\n    value.requires_grad = grad_value\n    sampling_locations.requires_grad = grad_sampling_loc\n    attention_weights.requires_grad = grad_attn_weight\n\n    gradok = gradcheck(func, (value.double(), shapes, level_start_index, sampling_locations.double(), attention_weights.double(), im2col_step))\n\n    print(f'* {gradok} check_gradient_numerical(D={channels})')\n\n\nif __name__ == '__main__':\n    check_forward_equal_with_pytorch_double()\n    check_forward_equal_with_pytorch_float()\n\n    for channels in [30, 32, 64, 71, 1025, 2048, 3096]:\n        check_gradient_numerical(channels, True, True, True)\n\n\n\n"
  },
  {
    "path": "models/layers/anyc_trans.py",
    "content": "\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom einops import repeat, rearrange, reduce\nfrom typing import Any, Optional\nfrom torch import Tensor\nfrom .utils import _get_activation_fn\n\nclass MLP(nn.Module):\n    \"\"\" Very simple multi-layer perceptron (also called FFN)\"\"\"\n    def __init__(self, input_dim, hidden_dim, output_dim, num_layers):\n        super().__init__()\n        self.num_layers = num_layers\n        h = [hidden_dim] * (num_layers - 1)\n        self.layers = nn.ModuleList(nn.Linear(n, k) for n, k in zip([input_dim] + h, h + [output_dim]))\n\n    def forward(self, x):\n        for i, layer in enumerate(self.layers):\n            x = F.relu(layer(x)) if i < self.num_layers - 1 else layer(x)\n        return x\n\nclass Linear_NormAct(nn.Linear):\n    def __init__(self, *args, **kwargs):\n        norm = kwargs.pop(\"norm\", None)\n        activation = kwargs.pop(\"activation\", None)\n        super().__init__(*args, **kwargs)\n        out_features = kwargs['out_features']\n        if norm == None:\n            self.norm = None\n        elif norm == 'ln':\n            self.norm = nn.LayerNorm(out_features)\n        else:\n            raise ValueError()\n        \n        self.activation = _get_activation_fn(activation)\n\n    def forward(self, x):\n        x = F.linear(x, self.weight, self.bias)\n        if self.norm is not None:\n            x = self.norm(x)\n        if self.activation is not None:\n            x = self.activation(x)\n        return x\n\nclass Conv2d_NormAct(torch.nn.Conv2d):\n    def __init__(self, *args, **kwargs):\n        norm = kwargs.pop(\"norm\", None)\n        activation = kwargs.pop(\"activation\", None)\n        super().__init__(*args, **kwargs)\n        out_dim = kwargs['out_channels']\n        if norm is None:\n            self.norm = None\n        elif norm == 'bn2d':\n            # b c h w\n            self.norm = nn.BatchNorm2d(out_dim)\n        elif 'gn' in norm:\n            # b c ..\n            n_groups = int(norm.split('_')[-1])\n            self.norm = nn.GroupNorm(n_groups, out_dim)\n        else:\n            raise ValueError()\n        self.activation = _get_activation_fn(activation)\n\n    def forward(self, x):\n        # b c h w\n        x = self._conv_forward(x, self.weight, self.bias)\n        if self.norm is not None:\n            x = self.norm(x)\n        if self.activation is not None:\n            x = self.activation(x)\n        return x\n\nclass Conv3d_NormAct(torch.nn.Conv3d):\n    def __init__(self, *args, **kwargs):\n        norm = kwargs.pop(\"norm\", None)\n        activation = kwargs.pop(\"activation\", None)\n        super().__init__(*args, **kwargs)\n        out_dim = kwargs['out_channels']\n        if norm == None:\n            self.norm = None\n        elif 'gn' in norm:\n            n_groups = int(norm.split('_')[-1])\n            self.norm = nn.GroupNorm(n_groups, out_dim)\n        else:\n            raise ValueError()\n        self.activation = _get_activation_fn(activation)\n\n    def forward(self, x):\n        x = self._conv_forward(x, self.weight, self.bias)\n        if self.norm is not None:\n            x = self.norm(x)\n        if self.activation is not None:\n            x = self.activation(x)\n        return x\n\n"
  },
  {
    "path": "models/layers/decoder_layers.py",
    "content": "\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom einops import repeat, rearrange, reduce\nfrom typing import Any, Optional\nfrom torch import Tensor\nfrom .utils import _get_activation_fn\n\nclass SelfAttentionLayer(nn.Module):\n\n    def __init__(self, d_model, nhead, dropout=0.0,\n                 activation=\"relu\", normalize_before=False):\n        super().__init__()\n        self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)\n\n        self.norm = nn.LayerNorm(d_model)\n        self.dropout = nn.Dropout(dropout)\n\n        self.activation = _get_activation_fn(activation)\n        self.normalize_before = normalize_before\n\n        self._reset_parameters()\n    \n    def _reset_parameters(self):\n        for p in self.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_uniform_(p)\n\n    def with_pos_embed(self, tensor, pos: Optional[Tensor]):\n        return tensor if pos is None else tensor + pos\n\n    def forward_post(self, tgt,\n                     tgt_mask: Optional[Tensor] = None,\n                     tgt_key_padding_mask: Optional[Tensor] = None,\n                     query_pos: Optional[Tensor] = None,\n                     ):\n        q = k = self.with_pos_embed(tgt, query_pos)\n        tgt2 = self.self_attn(q, k, value=tgt, attn_mask=tgt_mask,\n                              key_padding_mask=tgt_key_padding_mask)[0]\n        tgt = tgt + self.dropout(tgt2)\n        tgt = self.norm(tgt)\n        \n\n\n        return tgt\n\n    def forward_pre(self, tgt,\n                    tgt_mask: Optional[Tensor] = None,\n                    tgt_key_padding_mask: Optional[Tensor] = None,\n                    query_pos: Optional[Tensor] = None,\n                    ):\n        tgt2 = self.norm(tgt)\n        q = k = self.with_pos_embed(tgt2, query_pos)\n        tgt2 = self.self_attn(q, k, value=tgt2, attn_mask=tgt_mask,\n                              key_padding_mask=tgt_key_padding_mask)[0]\n        tgt = tgt + self.dropout(tgt2)\n        \n\n        \n        return tgt\n\n    def forward(self, tgt,\n                tgt_mask: Optional[Tensor] = None,\n                tgt_key_padding_mask: Optional[Tensor] = None,\n                query_pos: Optional[Tensor] = None,):\n        if self.normalize_before:\n            return self.forward_pre(tgt, tgt_mask,\n                                    tgt_key_padding_mask, query_pos)\n        return self.forward_post(tgt, tgt_mask,\n                                 tgt_key_padding_mask, query_pos)\n\nclass CrossAttentionLayer(nn.Module):\n\n    def __init__(self, d_model, nhead, dropout=0.0,\n                 activation=\"relu\", normalize_before=False):\n        super().__init__()\n        self.multihead_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)\n\n        self.norm = nn.LayerNorm(d_model)\n        self.dropout = nn.Dropout(dropout)\n\n        self.activation = _get_activation_fn(activation)\n        self.normalize_before = normalize_before\n    \n        self._reset_parameters()\n    \n    def _reset_parameters(self):\n        for p in self.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_uniform_(p)\n\n    def with_pos_embed(self, tensor, pos: Optional[Tensor]):\n        return tensor if pos is None else tensor + pos\n\n    def forward_post(self, tgt, memory,\n                     memory_mask: Optional[Tensor] = None,\n                     memory_key_padding_mask: Optional[Tensor] = None,\n                     pos: Optional[Tensor] = None,\n                     query_pos: Optional[Tensor] = None,\n                     ):\n        tgt2 = self.multihead_attn(query=self.with_pos_embed(tgt, query_pos),\n                                   key=self.with_pos_embed(memory, pos),\n                                   value=memory, attn_mask=memory_mask,\n                                   key_padding_mask=memory_key_padding_mask)[0]\n        tgt = tgt + self.dropout(tgt2)\n        tgt = self.norm(tgt)  # n b d\n\n        \n        return tgt\n\n    def forward_pre(self, tgt, memory,\n                    memory_mask: Optional[Tensor] = None,\n                    memory_key_padding_mask: Optional[Tensor] = None,\n                    pos: Optional[Tensor] = None,\n                    query_pos: Optional[Tensor] = None,\n                    ):\n        tgt2 = self.norm(tgt)\n        tgt2 = self.multihead_attn(query=self.with_pos_embed(tgt2, query_pos),\n                                   key=self.with_pos_embed(memory, pos),\n                                   value=memory, attn_mask=memory_mask,\n                                   key_padding_mask=memory_key_padding_mask)[0]\n        tgt = tgt + self.dropout(tgt2)\n        return tgt\n\n    def forward(self, tgt, memory,\n                memory_mask: Optional[Tensor] = None,\n                memory_key_padding_mask: Optional[Tensor] = None,\n                pos: Optional[Tensor] = None,\n                query_pos: Optional[Tensor] = None,\n                ):\n        if self.normalize_before:\n            return self.forward_pre(tgt, memory, memory_mask,\n                                    memory_key_padding_mask, pos, query_pos)\n        return self.forward_post(tgt, memory, memory_mask,\n                                 memory_key_padding_mask, pos, query_pos)\n\nclass FFNLayer(nn.Module):\n\n    def __init__(self, d_model, dim_feedforward=2048, dropout=0.0,\n                 activation=\"relu\", normalize_before=False):\n        super().__init__()\n        # Implementation of Feedforward model\n        self.linear1 = nn.Linear(d_model, dim_feedforward)\n        self.dropout = nn.Dropout(dropout)\n        self.linear2 = nn.Linear(dim_feedforward, d_model)\n\n        self.norm = nn.LayerNorm(d_model)\n\n        self.activation = _get_activation_fn(activation)\n        self.normalize_before = normalize_before\n\n        self._reset_parameters()\n    \n    def _reset_parameters(self):\n        for p in self.parameters():\n            if p.dim() > 1:\n                nn.init.xavier_uniform_(p)\n\n    def with_pos_embed(self, tensor, pos: Optional[Tensor]):\n        return tensor if pos is None else tensor + pos\n\n    def forward_post(self, tgt):\n        tgt2 = self.linear2(self.dropout(self.activation(self.linear1(tgt))))\n        tgt = tgt + self.dropout(tgt2)\n        tgt = self.norm(tgt)\n        return tgt\n\n    def forward_pre(self, tgt):\n        tgt2 = self.norm(tgt)\n        tgt2 = self.linear2(self.dropout(self.activation(self.linear1(tgt2))))\n        tgt = tgt + self.dropout(tgt2)\n        return tgt\n\n    def forward(self, tgt):\n        if self.normalize_before:\n            return self.forward_pre(tgt)\n        return self.forward_post(tgt)\n    \n"
  },
  {
    "path": "models/layers/gilbert/demo/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <title>Gilbert Curve</title>\n\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n  <script type='text/javascript' src='gilbert.js'></script>\n  <script type='text/javascript' src='two.js'></script>\n\n\n\n  <link rel=\"stylesheet\" href=\"normalize.css\">\n  <link rel=\"stylesheet\" href=\"skeleton.css\">\n\n</head>\n\n  <body>\n    <div class='container' style='margin-top:1em; text-align:center;'>\n\n      <h4>Gilbert Curve\n          <a class='u-pull-right' href='https://github.com/jakubcerveny/gilbert'>\n          <svg xmlns=\"http://www.w3.org/2000/svg\" width='15' height='15' opacity='0.5' version=\"1\" viewBox=\"0 0 99.083 100\">\n            <path d=\"M49.54 0C22.2-.018.026 22.375 0 49.974.025 77.62 22.198 100.01 49.54 100c27.355.01 49.524-22.38 49.543-50.026C99.063 22.374 76.893-.018 49.54 0zm0 88.48c-21.04 0-38.096-17.23-38.074-38.507C11.443 28.74 28.5 11.513 49.54 11.52c21.05-.008 38.11 17.222 38.132 38.454C87.65 71.25 70.59 88.48 49.542 88.48z\"/>\n            <g transform=\"translate(23.5,23) scale(6.5)\">\n              <path d=\"M 2.99 0 h 2.02 v 5.47 H 2.99 z m 0 6.39 h 2.02 V 8 H 2.99 z\"/>\n            </g>\n          </svg>\n        </a>\n      </h4>\n\n    </div>\n\n    <div class='container' style='margin-top:1em;'>\n\n\n      <div class='row' >\n        <div class='six columns'>\n          W <input id='ui_width' type='number' onchange='update_num();' oninput='update_num();' onclick='this.select();' >\n        </div>\n\n        <div class='six columns'>\n          H <input id='ui_height' type='number' oninput='update_num();' onclick='this.select();' >\n        </div>\n      </div>\n\n      <div class='row' style='height:6em;'>\n        <div class='six columns'>\n\n          <select id='ui_preset' onchange='update_preset();' >\n            <option value='custom'>custom</option>\n            <option value='55x31'>55x31</option>\n            <option value='100x63'>100x63</option>\n            <option value='15x12'>15x12</option>\n          </select>\n\n        </div>\n\n        <div class='six columns'>\n          <a id='ui_color' class='button' onclick='update_color();'  >rgb/bw</a>\n        </div>\n\n      </div>\n\n      <div class='row' >\n        <div class='twelve columns'>\n\n          <a id='ui_svg' class='button' onclick='dl_svg();'  >\n            <svg xmlns=\"http://www.w3.org/2000/svg\" width='15' height='15' opacity='0.8' viewBox=\"0 0 120 120\">\n              <path d=\"M48.732 69.783L91.04 27.476l11.74 11.74-42.308 42.31z\"/>\n              <path d=\"M51 3.424h19.054v60.21H51z\"/>\n              <path d=\"M60.543 81.572L18.22 39.282l11.72-11.74L72.27 69.85zM9 99.575h103v17H9z\"/>\n              <path d=\"M5.5 68.576h17v48h-17zm92 0h17v48h-17z\"/>\n            </svg>\n            SVG</a>\n\n        </div>\n      </div>\n\n    </div>\n\n    <div class='container'>\n      <div class='row'>\n        <div id='gilbert_container'></div>\n      </div>\n    </div>\n\n\n\n      <!--\n      <div class='row' style='height:6em;'>\n        <div class='four columns'>\n          W <input id='ui_width' type='number' style='width:80%;' onchange='update_num();' oninput='update_num();'>\n        </div>\n\n        <div class='four columns'>\n          <select id='ui_preset' onchange='update_preset();' >\n            <option value='custom'>custom</option>\n            <option value='55x31'>55x31</option>\n            <option value='100x63'>100x63</option>\n            <option value='15x12'>15x12</option>\n          </select>\n        </div>\n\n        <div class='four columns'>\n          <a id='ui_svg' class='button' onclick='dl_svg();'  >\n\n          <svg xmlns=\"http://www.w3.org/2000/svg\" width='15' height='15' opacity='0.8' viewBox=\"0 0 120 120\">\n            <path d=\"M48.732 69.783L91.04 27.476l11.74 11.74-42.308 42.31z\"/>\n            <path d=\"M51 3.424h19.054v60.21H51z\"/>\n            <path d=\"M60.543 81.572L18.22 39.282l11.72-11.74L72.27 69.85zM9 99.575h103v17H9z\"/>\n            <path d=\"M5.5 68.576h17v48h-17zm92 0h17v48h-17z\"/>\n          </svg>\n\n            SVG</a>\n        </div>\n\n      </div>\n\n      <div class='row'>\n        <div class='four columns' >\n          H\n          <input id='ui_height' type='number' style='width:80%;'onchange='update_num();' oninput='update_num();'>\n        </div>\n\n        <div class='four columns' >\n          &nbsp;\n        </div>\n\n        <div class='four columns'>\n          <a id='ui_color' class='button' onclick='update_color();'  >rgb/bw</a>\n        </div>\n\n      </div>\n      -->\n\n\n    <script defer='defer' src='script.js'></script>\n  </body>\n</html>\n\n"
  },
  {
    "path": "models/layers/gilbert/demo/normalize.css",
    "content": "/*! normalize.css v3.0.2 | MIT License | git.io/normalize */\n\n/**\n * 1. Set default font family to sans-serif.\n * 2. Prevent iOS text size adjust after orientation change, without disabling\n *    user zoom.\n */\n\nhtml {\n  font-family: sans-serif; /* 1 */\n  -ms-text-size-adjust: 100%; /* 2 */\n  -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/**\n * Remove default margin.\n */\n\nbody {\n  margin: 0;\n}\n\n/* HTML5 display definitions\n   ========================================================================== */\n\n/**\n * Correct `block` display not defined for any HTML5 element in IE 8/9.\n * Correct `block` display not defined for `details` or `summary` in IE 10/11\n * and Firefox.\n * Correct `block` display not defined for `main` in IE 11.\n */\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n  display: block;\n}\n\n/**\n * 1. Correct `inline-block` display not defined in IE 8/9.\n * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n */\n\naudio,\ncanvas,\nprogress,\nvideo {\n  display: inline-block; /* 1 */\n  vertical-align: baseline; /* 2 */\n}\n\n/**\n * Prevent modern browsers from displaying `audio` without controls.\n * Remove excess height in iOS 5 devices.\n */\n\naudio:not([controls]) {\n  display: none;\n  height: 0;\n}\n\n/**\n * Address `[hidden]` styling not present in IE 8/9/10.\n * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.\n */\n\n[hidden],\ntemplate {\n  display: none;\n}\n\n/* Links\n   ========================================================================== */\n\n/**\n * Remove the gray background color from active links in IE 10.\n */\n\na {\n  background-color: transparent;\n}\n\n/**\n * Improve readability when focused and also mouse hovered in all browsers.\n */\n\na:active,\na:hover {\n  outline: 0;\n}\n\n/* Text-level semantics\n   ========================================================================== */\n\n/**\n * Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n */\n\nabbr[title] {\n  border-bottom: 1px dotted;\n}\n\n/**\n * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n */\n\nb,\nstrong {\n  font-weight: bold;\n}\n\n/**\n * Address styling not present in Safari and Chrome.\n */\n\ndfn {\n  font-style: italic;\n}\n\n/**\n * Address variable `h1` font-size and margin within `section` and `article`\n * contexts in Firefox 4+, Safari, and Chrome.\n */\n\nh1 {\n  font-size: 2em;\n  margin: 0.67em 0;\n}\n\n/**\n * Address styling not present in IE 8/9.\n */\n\nmark {\n  background: #ff0;\n  color: #000;\n}\n\n/**\n * Address inconsistent and variable font size in all browsers.\n */\n\nsmall {\n  font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` affecting `line-height` in all browsers.\n */\n\nsub,\nsup {\n  font-size: 75%;\n  line-height: 0;\n  position: relative;\n  vertical-align: baseline;\n}\n\nsup {\n  top: -0.5em;\n}\n\nsub {\n  bottom: -0.25em;\n}\n\n/* Embedded content\n   ========================================================================== */\n\n/**\n * Remove border when inside `a` element in IE 8/9/10.\n */\n\nimg {\n  border: 0;\n}\n\n/**\n * Correct overflow not hidden in IE 9/10/11.\n */\n\nsvg:not(:root) {\n  overflow: hidden;\n}\n\n/* Grouping content\n   ========================================================================== */\n\n/**\n * Address margin not present in IE 8/9 and Safari.\n */\n\nfigure {\n  margin: 1em 40px;\n}\n\n/**\n * Address differences between Firefox and other browsers.\n */\n\nhr {\n  -moz-box-sizing: content-box;\n  box-sizing: content-box;\n  height: 0;\n}\n\n/**\n * Contain overflow in all browsers.\n */\n\npre {\n  overflow: auto;\n}\n\n/**\n * Address odd `em`-unit font size rendering in all browsers.\n */\n\ncode,\nkbd,\npre,\nsamp {\n  font-family: monospace, monospace;\n  font-size: 1em;\n}\n\n/* Forms\n   ========================================================================== */\n\n/**\n * Known limitation: by default, Chrome and Safari on OS X allow very limited\n * styling of `select`, unless a `border` property is set.\n */\n\n/**\n * 1. Correct color not being inherited.\n *    Known issue: affects color of disabled elements.\n * 2. Correct font properties not being inherited.\n * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n  color: inherit; /* 1 */\n  font: inherit; /* 2 */\n  margin: 0; /* 3 */\n}\n\n/**\n * Address `overflow` set to `hidden` in IE 8/9/10/11.\n */\n\nbutton {\n  overflow: visible;\n}\n\n/**\n * Address inconsistent `text-transform` inheritance for `button` and `select`.\n * All other form control elements do not inherit `text-transform` values.\n * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n * Correct `select` style inheritance in Firefox.\n */\n\nbutton,\nselect {\n  text-transform: none;\n}\n\n/**\n * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n *    and `video` controls.\n * 2. Correct inability to style clickable `input` types in iOS.\n * 3. Improve usability and consistency of cursor style between image-type\n *    `input` and others.\n */\n\nbutton,\nhtml input[type=\"button\"], /* 1 */\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n  -webkit-appearance: button; /* 2 */\n  cursor: pointer; /* 3 */\n}\n\n/**\n * Re-set default cursor for disabled elements.\n */\n\nbutton[disabled],\nhtml input[disabled] {\n  cursor: default;\n}\n\n/**\n * Remove inner padding and border in Firefox 4+.\n */\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n  border: 0;\n  padding: 0;\n}\n\n/**\n * Address Firefox 4+ setting `line-height` on `input` using `!important` in\n * the UA stylesheet.\n */\n\ninput {\n  line-height: normal;\n}\n\n/**\n * It's recommended that you don't attempt to style these elements.\n * Firefox's implementation doesn't respect box-sizing, padding, or width.\n *\n * 1. Address box sizing set to `content-box` in IE 8/9/10.\n * 2. Remove excess padding in IE 8/9/10.\n */\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  box-sizing: border-box; /* 1 */\n  padding: 0; /* 2 */\n}\n\n/**\n * Fix the cursor style for Chrome's increment/decrement buttons. For certain\n * `font-size` values of the `input`, it causes the cursor style of the\n * decrement button to change from `default` to `text`.\n */\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n  height: auto;\n}\n\n/**\n * 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n * 2. Address `box-sizing` set to `border-box` in Safari and Chrome\n *    (include `-moz` to future-proof).\n */\n\ninput[type=\"search\"] {\n  -webkit-appearance: textfield; /* 1 */\n  -moz-box-sizing: content-box;\n  -webkit-box-sizing: content-box; /* 2 */\n  box-sizing: content-box;\n}\n\n/**\n * Remove inner padding and search cancel button in Safari and Chrome on OS X.\n * Safari (but not Chrome) clips the cancel button when the search input has\n * padding (and `textfield` appearance).\n */\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n  -webkit-appearance: none;\n}\n\n/**\n * Define consistent border, margin, and padding.\n */\n\nfieldset {\n  border: 1px solid #c0c0c0;\n  margin: 0 2px;\n  padding: 0.35em 0.625em 0.75em;\n}\n\n/**\n * 1. Correct `color` not being inherited in IE 8/9/10/11.\n * 2. Remove padding so people aren't caught out if they zero out fieldsets.\n */\n\nlegend {\n  border: 0; /* 1 */\n  padding: 0; /* 2 */\n}\n\n/**\n * Remove default vertical scrollbar in IE 8/9/10/11.\n */\n\ntextarea {\n  overflow: auto;\n}\n\n/**\n * Don't inherit the `font-weight` (applied by a rule above).\n * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n */\n\noptgroup {\n  font-weight: bold;\n}\n\n/* Tables\n   ========================================================================== */\n\n/**\n * Remove most spacing between table cells.\n */\n\ntable {\n  border-collapse: collapse;\n  border-spacing: 0;\n}\n\ntd,\nth {\n  padding: 0;\n}"
  },
  {
    "path": "models/layers/gilbert/demo/script.js",
    "content": "// SPDX-License-Identifier: BSD-2-Clause\n// Copyright (c) 2024 abetusk\n\nvar info = {\n  \"W\" : -1,\n  \"H\": -1,\n  \"default\": { \"w\": 28, \"h\": 18 },\n  \"T\": { \"x\": 10, \"y\": 10 },\n  \"S\": { \"x\": 10, \"y\": 10 },\n  \"color\": \"color\",\n  \"reverse_y\": true,\n  \"two\": null,\n  \"line\": [],\n  \"two\": null,\n  \"ctx\": null,\n  \"container\" : null,\n  \"canvas\": null\n};\n\n\n//---\n// https://stackoverflow.com/a/18197341 CC-BY-SA \n// Matěj Pokorný (https://stackoverflow.com/users/2438165/mat%c4%9bj-pokorn%c3%bd)\n//\nfunction download(filename, text) {\n  let element = document.createElement('a');\n  element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));\n  element.setAttribute('download', filename);\n  element.style.display = 'none';\n  document.body.appendChild(element);\n  element.click();\n  document.body.removeChild(element);\n}\n//\n//---\n\nfunction dl_svg() {\n\n  info.two.render();\n  info.two.update();\n  info.svg = info.container.innerHTML;\n\n  let txt = info.svg.toString();\n  let wxh_str = info.W.toString() + \"x\" + info.H.toString();\n\n  download(\"gilbert\" + wxh_str + \".svg\", txt);\n\n}\n\nfunction update_color() {\n  if (info.color == \"color\") { info.color = \"bw\"; }\n  else { info.color = \"color\"; }\n\n  draw_curve();\n}\n\nfunction update_wh(w,h) {\n  let ui_width  = document.getElementById(\"ui_width\");\n  let ui_height = document.getElementById(\"ui_height\");\n\n  ui_width.value  = w;\n  ui_height.value = h\n\n  info.W = w;\n  info.H = h;\n\n  draw_curve();\n}\n\n\nfunction update_preset() {\n  let ele = document.getElementById(\"ui_preset\");\n  let v = ele.value;\n\n  if ( v.match( /^\\d+x\\d+$/ ) ){\n    let tok = v.split(\"x\");\n    let _w = parseInt(tok[0]);\n    let _h = parseInt(tok[1]);\n\n    if (isNaN(_w)) { _w = 1; }\n    if (isNaN(_h)) { _h = 1; }\n\n    if (_w < 1) { _w = 1; }\n    if (_h < 1) { _h = 1; }\n\n    update_wh(_w,_h);\n    return;\n  }\n\n  let ui_w = document.getElementById(\"ui_width\");\n  let ui_h = document.getElementById(\"ui_height\");\n\n  let _w = parseInt(ui_w.value);\n  let _h = parseInt(ui_h.value);\n\n  if (isNaN(_w)) { _w = 1; }\n  if (isNaN(_h)) { _h = 1; }\n\n  if (_w < 1) { _w = 1; }\n  if (_h < 1) { _h = 1; }\n\n  update_wh(_w,_h);\n\n}\n\nfunction update_num() {\n  let ui_w = document.getElementById(\"ui_width\");\n  let ui_h = document.getElementById(\"ui_height\");\n\n  let _w = parseInt(ui_w.value);\n  let _h = parseInt(ui_h.value);\n\n  if (isNaN(_w)) { _w = 1; }\n  if (isNaN(_h)) { _h = 1; }\n\n  if (_w < 1) { _w = 1; }\n  if (_h < 1) { _h = 1; }\n\n  update_wh(_w,_h);\n}\n\n\nfunction draw_curve() {\n  let two = info.two;\n  let W = info.W;\n  let H = info.H;\n\n  let S = info.S;\n  let T = info.T;\n\n  let flip = info.reverse_y;\n\n  //two.clear();\n\n  let N = (W*H);\n\n  let n = N-1;\n  if (n==0) { return; }\n\n  if (n < info.line.length) {\n    let m = info.line.length;\n    for (let ii=n; ii<m; ii++) {\n      info.line[ii].visible = false;\n      info.two.remove( info.line[ii] );\n    }\n    info.line = info.line.slice(0,n);\n  }\n\n  if (n > info.line.length) {\n    let m = info.line.length;\n    for (let ii=m; ii<n; ii++) {\n      let _line = two.makeLine(0,0,1,1);\n      _line.visible = false;\n      info.line.push( _line );\n    }\n  }\n\n  for (let idx=1; idx<(W*H); idx++) {\n    let q = gilbert.d2xy( idx-1, W, H );\n    let p = gilbert.d2xy( idx, W, H );\n\n    if (flip) {\n      p.y = H - p.y;\n      q.y = H - q.y;\n    }\n\n    q.x *= S.x; q.y *= S.y;\n    p.x *= S.x; p.y *= S.y;\n\n    q.x += T.x; q.y += T.y;\n    p.x += T.x; p.y += T.y;\n\n    info.line[idx-1].vertices[0].x = q.x;\n    info.line[idx-1].vertices[0].y = q.y;\n    info.line[idx-1].vertices[1].x = p.x;\n    info.line[idx-1].vertices[1].y = p.y;\n\n    info.line[idx-1].visible = true;\n\n    let clr = \"#666\";\n\n    if (info.color==\"color\") {\n\n      // imo, oklch looks a lot better, especially for the\n      // yellows, but inkscape is having problems rendering it.\n      // Kept here for posteririty but to actually get an\n      // exported SVG to render in inkscape, the HSL below\n      // is needed.\n      //\n      let hue = Math.floor(360*idx / (W*H)).toString();\n      let lit = '55%';\n      let crm = '0.35';\n      clr = 'oklch(' + [ lit,crm,hue ].join(\",\") + ')';\n\n\n      let sat = \"95%\";\n      lit = '35%';\n      clr = \"hsl(\" + [ hue,sat,lit ].join(\",\") + \")\";\n    }\n\n    info.line[idx-1].fill = clr;\n    info.line[idx-1].stroke = clr;\n  }\n\n  two.width = W*info.S.x*1.1;\n  two.height = H*info.S.y*1.1;\n\n  two.update();\n  two.render();\n  info.svg = info.container.innerHTML;\n}\n\n\nfunction init() {\n  let W = info.default.w,\n      H = info.default.h;\n\n  info.container = document.getElementById(\"gilbert_container\");\n  //info.two = new Two().appendTo(info.container);\n  info.two = new Two({ type: Two.Types.svg }).appendTo(info.container);\n\n  update_wh(W,H);\n}\n\ninit();\n"
  },
  {
    "path": "models/layers/gilbert/demo/skeleton.css",
    "content": "/*\n* Skeleton V2.0.4\n* Copyright 2014, Dave Gamache\n* www.getskeleton.com\n* Free to use under the MIT license.\n* http://www.opensource.org/licenses/mit-license.php\n* 12/29/2014\n*/\n\n\n/* Table of contents\n––––––––––––––––––––––––––––––––––––––––––––––––––\n- Grid\n- Base Styles\n- Typography\n- Links\n- Buttons\n- Forms\n- Lists\n- Code\n- Tables\n- Spacing\n- Utilities\n- Clearing\n- Media Queries\n*/\n\n\n/* Grid\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\n.container {\n  position: relative;\n  width: 100%;\n  max-width: 960px;\n  margin: 0 auto;\n  padding: 0 20px;\n  box-sizing: border-box; }\n.column,\n.columns {\n  width: 100%;\n  float: left;\n  box-sizing: border-box; }\n\n/* For devices larger than 400px */\n@media (min-width: 400px) {\n  .container {\n    width: 85%;\n    padding: 0; }\n}\n\n/* For devices larger than 550px */\n@media (min-width: 550px) {\n  .container {\n    width: 80%; }\n  .column,\n  .columns {\n    margin-left: 4%; }\n  .column:first-child,\n  .columns:first-child {\n    margin-left: 0; }\n\n  .one.column,\n  .one.columns                    { width: 4.66666666667%; }\n  .two.columns                    { width: 13.3333333333%; }\n  .three.columns                  { width: 22%;            }\n  .four.columns                   { width: 30.6666666667%; }\n  .five.columns                   { width: 39.3333333333%; }\n  .six.columns                    { width: 48%;            }\n  .seven.columns                  { width: 56.6666666667%; }\n  .eight.columns                  { width: 65.3333333333%; }\n  .nine.columns                   { width: 74.0%;          }\n  .ten.columns                    { width: 82.6666666667%; }\n  .eleven.columns                 { width: 91.3333333333%; }\n  .twelve.columns                 { width: 100%; margin-left: 0; }\n\n  .one-third.column               { width: 30.6666666667%; }\n  .two-thirds.column              { width: 65.3333333333%; }\n\n  .one-half.column                { width: 48%; }\n\n  /* Offsets */\n  .offset-by-one.column,\n  .offset-by-one.columns          { margin-left: 8.66666666667%; }\n  .offset-by-two.column,\n  .offset-by-two.columns          { margin-left: 17.3333333333%; }\n  .offset-by-three.column,\n  .offset-by-three.columns        { margin-left: 26%;            }\n  .offset-by-four.column,\n  .offset-by-four.columns         { margin-left: 34.6666666667%; }\n  .offset-by-five.column,\n  .offset-by-five.columns         { margin-left: 43.3333333333%; }\n  .offset-by-six.column,\n  .offset-by-six.columns          { margin-left: 52%;            }\n  .offset-by-seven.column,\n  .offset-by-seven.columns        { margin-left: 60.6666666667%; }\n  .offset-by-eight.column,\n  .offset-by-eight.columns        { margin-left: 69.3333333333%; }\n  .offset-by-nine.column,\n  .offset-by-nine.columns         { margin-left: 78.0%;          }\n  .offset-by-ten.column,\n  .offset-by-ten.columns          { margin-left: 86.6666666667%; }\n  .offset-by-eleven.column,\n  .offset-by-eleven.columns       { margin-left: 95.3333333333%; }\n\n  .offset-by-one-third.column,\n  .offset-by-one-third.columns    { margin-left: 34.6666666667%; }\n  .offset-by-two-thirds.column,\n  .offset-by-two-thirds.columns   { margin-left: 69.3333333333%; }\n\n  .offset-by-one-half.column,\n  .offset-by-one-half.columns     { margin-left: 52%; }\n\n}\n\n\n/* Base Styles\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\n/* NOTE\nhtml is set to 62.5% so that all the REM measurements throughout Skeleton\nare based on 10px sizing. So basically 1.5rem = 15px :) */\nhtml {\n  font-size: 62.5%; }\nbody {\n  font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */\n  line-height: 1.6;\n  font-weight: 400;\n  font-family: \"Raleway\", \"HelveticaNeue\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  color: #222; }\n\n\n/* Typography\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\nh1, h2, h3, h4, h5, h6 {\n  margin-top: 0;\n  margin-bottom: 2rem;\n  font-weight: 300; }\nh1 { font-size: 4.0rem; line-height: 1.2;  letter-spacing: -.1rem;}\nh2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; }\nh3 { font-size: 3.0rem; line-height: 1.3;  letter-spacing: -.1rem; }\nh4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; }\nh5 { font-size: 1.8rem; line-height: 1.5;  letter-spacing: -.05rem; }\nh6 { font-size: 1.5rem; line-height: 1.6;  letter-spacing: 0; }\n\n/* Larger than phablet */\n@media (min-width: 550px) {\n  h1 { font-size: 5.0rem; }\n  h2 { font-size: 4.2rem; }\n  h3 { font-size: 3.6rem; }\n  h4 { font-size: 3.0rem; }\n  h5 { font-size: 2.4rem; }\n  h6 { font-size: 1.5rem; }\n}\n\np {\n  margin-top: 0; }\n\n\n/* Links\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\na {\n  color: #1EAEDB; }\na:hover {\n  color: #0FA0CE; }\n\n\n/* Buttons\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\n.button,\nbutton,\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"] {\n  display: inline-block;\n  height: 38px;\n  padding: 0 30px;\n  color: #555;\n  text-align: center;\n  font-size: 11px;\n  font-weight: 600;\n  line-height: 38px;\n  letter-spacing: .1rem;\n  text-transform: uppercase;\n  text-decoration: none;\n  white-space: nowrap;\n  background-color: transparent;\n  border-radius: 4px;\n  border: 1px solid #bbb;\n  cursor: pointer;\n  box-sizing: border-box; }\n.button:hover,\nbutton:hover,\ninput[type=\"submit\"]:hover,\ninput[type=\"reset\"]:hover,\ninput[type=\"button\"]:hover,\n.button:focus,\nbutton:focus,\ninput[type=\"submit\"]:focus,\ninput[type=\"reset\"]:focus,\ninput[type=\"button\"]:focus {\n  color: #333;\n  border-color: #888;\n  outline: 0; }\n.button.button-primary,\nbutton.button-primary,\ninput[type=\"submit\"].button-primary,\ninput[type=\"reset\"].button-primary,\ninput[type=\"button\"].button-primary {\n  color: #FFF;\n  background-color: #33C3F0;\n  border-color: #33C3F0; }\n.button.button-primary:hover,\nbutton.button-primary:hover,\ninput[type=\"submit\"].button-primary:hover,\ninput[type=\"reset\"].button-primary:hover,\ninput[type=\"button\"].button-primary:hover,\n.button.button-primary:focus,\nbutton.button-primary:focus,\ninput[type=\"submit\"].button-primary:focus,\ninput[type=\"reset\"].button-primary:focus,\ninput[type=\"button\"].button-primary:focus {\n  color: #FFF;\n  background-color: #1EAEDB;\n  border-color: #1EAEDB; }\n\n\n/* Forms\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\ninput[type=\"email\"],\ninput[type=\"number\"],\ninput[type=\"search\"],\ninput[type=\"text\"],\ninput[type=\"tel\"],\ninput[type=\"url\"],\ninput[type=\"password\"],\ntextarea,\nselect {\n  height: 38px;\n  padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */\n  background-color: #fff;\n  border: 1px solid #D1D1D1;\n  border-radius: 4px;\n  box-shadow: none;\n  box-sizing: border-box; }\n/* Removes awkward default styles on some inputs for iOS */\ninput[type=\"email\"],\ninput[type=\"number\"],\ninput[type=\"search\"],\ninput[type=\"text\"],\ninput[type=\"tel\"],\ninput[type=\"url\"],\ninput[type=\"password\"],\ntextarea {\n  -webkit-appearance: none;\n     -moz-appearance: none;\n          appearance: none; }\ntextarea {\n  min-height: 65px;\n  padding-top: 6px;\n  padding-bottom: 6px; }\ninput[type=\"email\"]:focus,\ninput[type=\"number\"]:focus,\ninput[type=\"search\"]:focus,\ninput[type=\"text\"]:focus,\ninput[type=\"tel\"]:focus,\ninput[type=\"url\"]:focus,\ninput[type=\"password\"]:focus,\ntextarea:focus,\nselect:focus {\n  border: 1px solid #33C3F0;\n  outline: 0; }\nlabel,\nlegend {\n  display: block;\n  margin-bottom: .5rem;\n  font-weight: 600; }\nfieldset {\n  padding: 0;\n  border-width: 0; }\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n  display: inline; }\nlabel > .label-body {\n  display: inline-block;\n  margin-left: .5rem;\n  font-weight: normal; }\n\n\n/* Lists\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\nul {\n  list-style: circle inside; }\nol {\n  list-style: decimal inside; }\nol, ul {\n  padding-left: 0;\n  margin-top: 0; }\nul ul,\nul ol,\nol ol,\nol ul {\n  margin: 1.5rem 0 1.5rem 3rem;\n  font-size: 90%; }\nli {\n  margin-bottom: 1rem; }\n\n\n/* Code\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\ncode {\n  padding: .2rem .5rem;\n  margin: 0 .2rem;\n  font-size: 90%;\n  white-space: nowrap;\n  background: #F1F1F1;\n  border: 1px solid #E1E1E1;\n  border-radius: 4px; }\npre > code {\n  display: block;\n  padding: 1rem 1.5rem;\n  white-space: pre; }\n\n\n/* Tables\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\nth,\ntd {\n  padding: 12px 15px;\n  text-align: left;\n  border-bottom: 1px solid #E1E1E1; }\nth:first-child,\ntd:first-child {\n  padding-left: 0; }\nth:last-child,\ntd:last-child {\n  padding-right: 0; }\n\n\n/* Spacing\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\nbutton,\n.button {\n  margin-bottom: 1rem; }\ninput,\ntextarea,\nselect,\nfieldset {\n  margin-bottom: 1.5rem; }\npre,\nblockquote,\ndl,\nfigure,\ntable,\np,\nul,\nol,\nform {\n  margin-bottom: 2.5rem; }\n\n\n/* Utilities\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\n.u-full-width {\n  width: 100%;\n  box-sizing: border-box; }\n.u-max-full-width {\n  max-width: 100%;\n  box-sizing: border-box; }\n.u-pull-right {\n  float: right; }\n.u-pull-left {\n  float: left; }\n\n\n/* Misc\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\nhr {\n  margin-top: 3rem;\n  margin-bottom: 3.5rem;\n  border-width: 0;\n  border-top: 1px solid #E1E1E1; }\n\n\n/* Clearing\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\n\n/* Self Clearing Goodness */\n.container:after,\n.row:after,\n.u-cf {\n  content: \"\";\n  display: table;\n  clear: both; }\n\n\n/* Media Queries\n–––––––––––––––––––––––––––––––––––––––––––––––––– */\n/*\nNote: The best way to structure the use of media queries is to create the queries\nnear the relevant code. For example, if you wanted to change the styles for buttons\non small devices, paste the mobile query code up in the buttons section and style it\nthere.\n*/\n\n\n/* Larger than mobile */\n@media (min-width: 400px) {}\n\n/* Larger than phablet (also point when grid becomes active) */\n@media (min-width: 550px) {}\n\n/* Larger than tablet */\n@media (min-width: 750px) {}\n\n/* Larger than desktop */\n@media (min-width: 1000px) {}\n\n/* Larger than Desktop HD */\n@media (min-width: 1200px) {}\n"
  },
  {
    "path": "models/layers/gilbert/demo/two.js",
    "content": "/*\nMIT License\n\nCopyright (c) 2012 - 2021 @jonobr1 / http://jono.fyi\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*/\nvar Two = (() => {\n  var __defProp = Object.defineProperty;\n  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;\n  var __getOwnPropNames = Object.getOwnPropertyNames;\n  var __hasOwnProp = Object.prototype.hasOwnProperty;\n  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;\n  var __export = (target, all) => {\n    for (var name in all)\n      __defProp(target, name, { get: all[name], enumerable: true });\n  };\n  var __copyProps = (to, from, except, desc) => {\n    if (from && typeof from === \"object\" || typeof from === \"function\") {\n      for (let key of __getOwnPropNames(from))\n        if (!__hasOwnProp.call(to, key) && key !== except)\n          __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n    }\n    return to;\n  };\n  var __toCommonJS = (mod2) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod2);\n  var __publicField = (obj, key, value) => {\n    __defNormalProp(obj, typeof key !== \"symbol\" ? key + \"\" : key, value);\n    return value;\n  };\n\n  // src/two.js\n  var two_exports = {};\n  __export(two_exports, {\n    default: () => Two\n  });\n\n  // src/utils/path-commands.js\n  var Commands = {\n    move: \"M\",\n    line: \"L\",\n    curve: \"C\",\n    arc: \"A\",\n    close: \"Z\"\n  };\n\n  // src/utils/math.js\n  var math_exports = {};\n  __export(math_exports, {\n    HALF_PI: () => HALF_PI,\n    NumArray: () => NumArray,\n    TWO_PI: () => TWO_PI,\n    decomposeMatrix: () => decomposeMatrix,\n    getComputedMatrix: () => getComputedMatrix,\n    getPoT: () => getPoT,\n    lerp: () => lerp,\n    mod: () => mod,\n    setMatrix: () => setMatrix,\n    toFixed: () => toFixed\n  });\n\n  // src/utils/root.js\n  var root;\n  if (typeof window !== \"undefined\") {\n    root = window;\n  } else if (typeof global !== \"undefined\") {\n    root = global;\n  } else if (typeof self !== \"undefined\") {\n    root = self;\n  }\n\n  // src/utils/math.js\n  var Matrix;\n  var TWO_PI = Math.PI * 2;\n  var HALF_PI = Math.PI * 0.5;\n  function decomposeMatrix(matrix, b, c, d, e, f) {\n    let a;\n    if (arguments.length <= 1) {\n      a = matrix.a;\n      b = matrix.b;\n      c = matrix.c;\n      d = matrix.d;\n      e = matrix.e;\n      f = matrix.f;\n    } else {\n      a = matrix;\n    }\n    return {\n      translateX: e,\n      translateY: f,\n      scaleX: Math.sqrt(a * a + b * b),\n      scaleY: Math.sqrt(c * c + d * d),\n      rotation: 180 * Math.atan2(b, a) / Math.PI\n    };\n  }\n  function setMatrix(matrix) {\n    Matrix = matrix;\n  }\n  function getComputedMatrix(object, matrix) {\n    matrix = matrix && matrix.identity() || new Matrix();\n    let parent = object;\n    const matrices = [];\n    while (parent && parent._matrix) {\n      matrices.push(parent._matrix);\n      parent = parent.parent;\n    }\n    matrices.reverse();\n    for (let i = 0; i < matrices.length; i++) {\n      const m = matrices[i];\n      const e = m.elements;\n      matrix.multiply(\n        e[0],\n        e[1],\n        e[2],\n        e[3],\n        e[4],\n        e[5],\n        e[6],\n        e[7],\n        e[8],\n        e[9]\n      );\n    }\n    return matrix;\n  }\n  function lerp(a, b, t) {\n    return t * (b - a) + a;\n  }\n  var pots = [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096];\n  function getPoT(value) {\n    let i = 0;\n    while (pots[i] && pots[i] < value) {\n      i++;\n    }\n    return pots[i];\n  }\n  function mod(v, l) {\n    while (v < 0) {\n      v += l;\n    }\n    return v % l;\n  }\n  var NumArray = root.Float32Array || Array;\n  var floor = Math.floor;\n  function toFixed(v) {\n    return floor(v * 1e6) / 1e6;\n  }\n\n  // src/utils/curves.js\n  var curves_exports = {};\n  __export(curves_exports, {\n    Curve: () => Curve,\n    getAnchorsFromArcData: () => getAnchorsFromArcData,\n    getComponentOnCubicBezier: () => getComponentOnCubicBezier,\n    getControlPoints: () => getControlPoints,\n    getCurveBoundingBox: () => getCurveBoundingBox,\n    getCurveFromPoints: () => getCurveFromPoints,\n    getCurveLength: () => getCurveLength,\n    getReflection: () => getReflection,\n    integrate: () => integrate,\n    subdivide: () => subdivide\n  });\n\n  // src/events.js\n  var Events = class {\n    _events = {};\n    _bound = false;\n    constructor() {\n    }\n    addEventListener(name, handler) {\n      const list = this._events[name] || (this._events[name] = []);\n      list.push(handler);\n      this._bound = true;\n      return this;\n    }\n    on() {\n      return this.addEventListener.apply(this, arguments);\n    }\n    bind() {\n      return this.addEventListener.apply(this, arguments);\n    }\n    removeEventListener(name, handler) {\n      if (!this._events) {\n        return this;\n      }\n      if (!name && !handler) {\n        this._events = {};\n        this._bound = false;\n        return this;\n      }\n      const names = name ? [name] : Object.keys(this._events);\n      for (let i = 0, l = names.length; i < l; i++) {\n        name = names[i];\n        let list = this._events[name];\n        if (list) {\n          let events = [];\n          if (handler) {\n            for (let j = 0, k = list.length; j < k; j++) {\n              let e = list[j];\n              e = e.handler ? e.handler : e;\n              if (handler !== e) {\n                events.push(e);\n              }\n            }\n          }\n          this._events[name] = events;\n        }\n      }\n      return this;\n    }\n    off() {\n      return this.removeEventListener.apply(this, arguments);\n    }\n    unbind() {\n      return this.removeEventListener.apply(this, arguments);\n    }\n    dispatchEvent(name) {\n      if (!this._events) {\n        return this;\n      }\n      const args = Array.prototype.slice.call(arguments, 1);\n      const events = this._events[name];\n      if (events) {\n        for (let i = 0; i < events.length; i++) {\n          events[i].call(this, ...args);\n        }\n      }\n      return this;\n    }\n    trigger() {\n      return this.dispatchEvent.apply(this, arguments);\n    }\n    listen(obj, name, handler) {\n      const scope = this;\n      if (obj) {\n        e.obj = obj;\n        e.name = name;\n        e.handler = handler;\n        obj.on(name, e);\n      }\n      function e() {\n        handler.apply(scope, arguments);\n      }\n      return scope;\n    }\n    ignore(obj, name, handler) {\n      obj.off(name, handler);\n      return this;\n    }\n  };\n  __publicField(Events, \"Types\", {\n    play: \"play\",\n    pause: \"pause\",\n    update: \"update\",\n    render: \"render\",\n    resize: \"resize\",\n    change: \"change\",\n    remove: \"remove\",\n    insert: \"insert\",\n    order: \"order\",\n    load: \"load\"\n  });\n  __publicField(Events, \"Methods\", [\n    \"addEventListener\",\n    \"on\",\n    \"removeEventListener\",\n    \"off\",\n    \"unbind\",\n    \"dispatchEvent\",\n    \"trigger\",\n    \"listen\",\n    \"ignore\"\n  ]);\n\n  // src/vector.js\n  var proto = {\n    x: {\n      enumerable: true,\n      get: function() {\n        return this._x;\n      },\n      set: function(v) {\n        if (this._x !== v) {\n          this._x = v;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    },\n    y: {\n      enumerable: true,\n      get: function() {\n        return this._y;\n      },\n      set: function(v) {\n        if (this._y !== v) {\n          this._y = v;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    }\n  };\n  var _Vector = class extends Events {\n    _x = 0;\n    _y = 0;\n    constructor(x = 0, y = 0) {\n      super();\n      for (let prop in proto) {\n        Object.defineProperty(this, prop, proto[prop]);\n      }\n      this.x = x;\n      this.y = y;\n    }\n    static add(v1, v2) {\n      return new _Vector(v1.x + v2.x, v1.y + v2.y);\n    }\n    static sub(v1, v2) {\n      return new _Vector(v1.x - v2.x, v1.y - v2.y);\n    }\n    static subtract(v1, v2) {\n      return _Vector.sub(v1, v2);\n    }\n    static ratioBetween(v1, v2) {\n      return (v1.x * v2.x + v1.y * v2.y) / (v1.length() * v2.length());\n    }\n    static angleBetween(v1, v2) {\n      if (arguments.length >= 4) {\n        const dx2 = arguments[0] - arguments[2];\n        const dy2 = arguments[1] - arguments[3];\n        return Math.atan2(dy2, dx2);\n      }\n      const dx = v1.x - v2.x;\n      const dy = v1.y - v2.y;\n      return Math.atan2(dy, dx);\n    }\n    static distanceBetween(v1, v2) {\n      return Math.sqrt(_Vector.distanceBetweenSquared(v1, v2));\n    }\n    static distanceBetweenSquared(v1, v2) {\n      const dx = v1.x - v2.x;\n      const dy = v1.y - v2.y;\n      return dx * dx + dy * dy;\n    }\n    set(x, y) {\n      this.x = x;\n      this.y = y;\n      return this;\n    }\n    copy(v) {\n      this.x = v.x;\n      this.y = v.y;\n      return this;\n    }\n    clear() {\n      this.x = 0;\n      this.y = 0;\n      return this;\n    }\n    clone() {\n      return new _Vector(this.x, this.y);\n    }\n    add(x, y) {\n      if (arguments.length <= 0) {\n        return this;\n      } else if (arguments.length <= 1) {\n        if (typeof x === \"number\") {\n          this.x += x;\n          this.y += x;\n        } else if (x && typeof x.x === \"number\" && typeof x.y === \"number\") {\n          this.x += x.x;\n          this.y += x.y;\n        }\n      } else {\n        this.x += x;\n        this.y += y;\n      }\n      return this;\n    }\n    addSelf(v) {\n      return this.add.apply(this, arguments);\n    }\n    sub(x, y) {\n      if (arguments.length <= 0) {\n        return this;\n      } else if (arguments.length <= 1) {\n        if (typeof x === \"number\") {\n          this.x -= x;\n          this.y -= x;\n        } else if (x && typeof x.x === \"number\" && typeof x.y === \"number\") {\n          this.x -= x.x;\n          this.y -= x.y;\n        }\n      } else {\n        this.x -= x;\n        this.y -= y;\n      }\n      return this;\n    }\n    subtract() {\n      return this.sub.apply(this, arguments);\n    }\n    subSelf(v) {\n      return this.sub.apply(this, arguments);\n    }\n    subtractSelf(v) {\n      return this.sub.apply(this, arguments);\n    }\n    multiply(x, y) {\n      if (arguments.length <= 0) {\n        return this;\n      } else if (arguments.length <= 1) {\n        if (typeof x === \"number\") {\n          this.x *= x;\n          this.y *= x;\n        } else if (x && typeof x.x === \"number\" && typeof x.y === \"number\") {\n          this.x *= x.x;\n          this.y *= x.y;\n        }\n      } else {\n        this.x *= x;\n        this.y *= y;\n      }\n      return this;\n    }\n    multiplySelf(v) {\n      return this.multiply.apply(this, arguments);\n    }\n    multiplyScalar(s) {\n      return this.multiply(s);\n    }\n    divide(x, y) {\n      if (arguments.length <= 0) {\n        return this;\n      } else if (arguments.length <= 1) {\n        if (typeof x === \"number\") {\n          this.x /= x;\n          this.y /= x;\n        } else if (x && typeof x.x === \"number\" && typeof x.y === \"number\") {\n          this.x /= x.x;\n          this.y /= x.y;\n        }\n      } else {\n        this.x /= x;\n        this.y /= y;\n      }\n      if (isNaN(this.x)) {\n        this.x = 0;\n      }\n      if (isNaN(this.y)) {\n        this.y = 0;\n      }\n      return this;\n    }\n    divideSelf(v) {\n      return this.divide.apply(this, arguments);\n    }\n    divideScalar(s) {\n      return this.divide(s);\n    }\n    negate() {\n      return this.multiply(-1);\n    }\n    dot(v) {\n      return this.x * v.x + this.y * v.y;\n    }\n    length() {\n      return Math.sqrt(this.lengthSquared());\n    }\n    lengthSquared() {\n      return this.x * this.x + this.y * this.y;\n    }\n    normalize() {\n      return this.divideScalar(this.length());\n    }\n    distanceTo(v) {\n      return Math.sqrt(this.distanceToSquared(v));\n    }\n    distanceToSquared(v) {\n      const dx = this.x - v.x;\n      const dy = this.y - v.y;\n      return dx * dx + dy * dy;\n    }\n    setLength(l) {\n      return this.normalize().multiplyScalar(l);\n    }\n    equals(v, eps) {\n      eps = typeof eps === \"undefined\" ? 1e-4 : eps;\n      return this.distanceTo(v) < eps;\n    }\n    lerp(v, t) {\n      const x = (v.x - this.x) * t + this.x;\n      const y = (v.y - this.y) * t + this.y;\n      return this.set(x, y);\n    }\n    isZero(eps) {\n      eps = typeof eps === \"undefined\" ? 1e-4 : eps;\n      return this.length() < eps;\n    }\n    toString() {\n      return this.x + \", \" + this.y;\n    }\n    toObject() {\n      return { x: this.x, y: this.y };\n    }\n    rotate(radians) {\n      const x = this.x;\n      const y = this.y;\n      const cos7 = Math.cos(radians);\n      const sin7 = Math.sin(radians);\n      this.x = x * cos7 - y * sin7;\n      this.y = x * sin7 + y * cos7;\n      return this;\n    }\n  };\n  var Vector = _Vector;\n  __publicField(Vector, \"zero\", new _Vector());\n  __publicField(Vector, \"left\", new _Vector(-1, 0));\n  __publicField(Vector, \"right\", new _Vector(1, 0));\n  __publicField(Vector, \"up\", new _Vector(0, -1));\n  __publicField(Vector, \"down\", new _Vector(0, 1));\n\n  // src/anchor.js\n  var Anchor = class extends Vector {\n    controls = {\n      left: new Vector(),\n      right: new Vector()\n    };\n    _command = Commands.move;\n    _relative = true;\n    _rx = 0;\n    _ry = 0;\n    _xAxisRotation = 0;\n    _largeArcFlag = 0;\n    _sweepFlag = 1;\n    constructor(x = 0, y = 0, ax = 0, ay = 0, bx = 0, by = 0, command = Commands.move) {\n      super(x, y);\n      for (let prop in proto2) {\n        Object.defineProperty(this, prop, proto2[prop]);\n      }\n      this.command = command;\n      this.relative = true;\n      const broadcast = Anchor.makeBroadcast(this);\n      this.controls.left.set(ax, ay).addEventListener(Events.Types.change, broadcast);\n      this.controls.right.set(bx, by).addEventListener(Events.Types.change, broadcast);\n    }\n    static makeBroadcast(scope) {\n      return broadcast;\n      function broadcast() {\n        if (scope._bound) {\n          scope.dispatchEvent(Events.Types.change);\n        }\n      }\n    }\n    copy(v) {\n      this.x = v.x;\n      this.y = v.y;\n      if (typeof v.command === \"string\") {\n        this.command = v.command;\n      }\n      if (v.controls) {\n        if (v.controls.left) {\n          this.controls.left.copy(v.controls.left);\n        }\n        if (v.controls.right) {\n          this.controls.right.copy(v.controls.right);\n        }\n      }\n      if (typeof v.relative === \"boolean\") {\n        this.relative = v.relative;\n      }\n      if (typeof v.rx === \"number\") {\n        this.rx = v.rx;\n      }\n      if (typeof v.ry === \"number\") {\n        this.ry = v.ry;\n      }\n      if (typeof v.xAxisRotation === \"number\") {\n        this.xAxisRotation = v.xAxisRotation;\n      }\n      if (typeof v.largeArcFlag === \"number\") {\n        this.largeArcFlag = v.largeArcFlag;\n      }\n      if (typeof v.sweepFlag === \"number\") {\n        this.sweepFlag = v.sweepFlag;\n      }\n      return this;\n    }\n    clone() {\n      return new Anchor().copy(this);\n    }\n    toObject() {\n      return {\n        x: this.x,\n        y: this.y,\n        command: this.command,\n        relative: this.relative,\n        controls: {\n          left: this.controls.left.toObject(),\n          right: this.controls.right.toObject()\n        },\n        rx: this.rx,\n        ry: this.ry,\n        xAxisRotation: this.xAxisRotation,\n        largeArcFlag: this.largeArcFlag,\n        sweepFlag: this.sweepFlag\n      };\n    }\n    toString() {\n      return JSON.stringify(this.toObject());\n    }\n  };\n  var proto2 = {\n    command: {\n      enumerable: true,\n      get: function() {\n        return this._command;\n      },\n      set: function(command) {\n        if (this._command !== command) {\n          this._command = command;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    },\n    relative: {\n      enumerable: true,\n      get: function() {\n        return this._relative;\n      },\n      set: function(relative) {\n        if (this._relative !== !!relative) {\n          this._relative = !!relative;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    },\n    rx: {\n      enumerable: true,\n      get: function() {\n        return this._rx;\n      },\n      set: function(rx) {\n        if (this._rx !== rx) {\n          this._rx = rx;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    },\n    ry: {\n      enumerable: true,\n      get: function() {\n        return this._ry;\n      },\n      set: function(ry) {\n        if (this._ry !== ry) {\n          this._ry = ry;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    },\n    xAxisRotation: {\n      enumerable: true,\n      get: function() {\n        return this._xAxisRotation;\n      },\n      set: function(xAxisRotation) {\n        if (this._xAxisRotation !== xAxisRotation) {\n          this._xAxisRotation = xAxisRotation;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    },\n    largeArcFlag: {\n      enumerable: true,\n      get: function() {\n        return this._largeArcFlag;\n      },\n      set: function(largeArcFlag) {\n        if (this._largeArcFlag !== largeArcFlag) {\n          this._largeArcFlag = largeArcFlag;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    },\n    sweepFlag: {\n      get: function() {\n        return this._sweepFlag;\n      },\n      set: function(sweepFlag) {\n        if (this._sweepFlag !== sweepFlag) {\n          this._sweepFlag = sweepFlag;\n          if (this._bound) {\n            this.dispatchEvent(Events.Types.change);\n          }\n        }\n      }\n    }\n  };\n\n  // src/constants.js\n  var count = 0;\n  var Constants = {\n    nextFrameID: null,\n    Types: {\n      webgl: \"WebGLRenderer\",\n      svg: \"SVGRenderer\",\n      canvas: \"CanvasRenderer\"\n    },\n    Version: \"v0.8.12\",\n    PublishDate: \"2023-10-16T17:55:26.551Z\",\n    Identifier: \"two-\",\n    Resolution: 12,\n    AutoCalculateImportedMatrices: true,\n    Instances: [],\n    uniqueId: function() {\n      return count++;\n    }\n  };\n\n  // src/utils/curves.js\n  var Curve = {\n    CollinearityEpsilon: Math.pow(10, -30),\n    RecursionLimit: 16,\n    CuspLimit: 0,\n    Tolerance: {\n      distance: 0.25,\n      angle: 0,\n      epsilon: Number.EPSILON\n    },\n    abscissas: [\n      [0.5773502691896257],\n      [0, 0.7745966692414834],\n      [0.33998104358485626, 0.8611363115940526],\n      [0, 0.5384693101056831, 0.906179845938664],\n      [0.2386191860831969, 0.6612093864662645, 0.932469514203152],\n      [0, 0.4058451513773972, 0.7415311855993945, 0.9491079123427585],\n      [0.1834346424956498, 0.525532409916329, 0.7966664774136267, 0.9602898564975363],\n      [0, 0.3242534234038089, 0.6133714327005904, 0.8360311073266358, 0.9681602395076261],\n      [0.14887433898163122, 0.4333953941292472, 0.6794095682990244, 0.8650633666889845, 0.9739065285171717],\n      [0, 0.26954315595234496, 0.5190961292068118, 0.7301520055740494, 0.8870625997680953, 0.978228658146057],\n      [0.1252334085114689, 0.3678314989981802, 0.5873179542866175, 0.7699026741943047, 0.9041172563704749, 0.9815606342467192],\n      [0, 0.2304583159551348, 0.44849275103644687, 0.6423493394403402, 0.8015780907333099, 0.9175983992229779, 0.9841830547185881],\n      [0.10805494870734367, 0.31911236892788974, 0.5152486363581541, 0.6872929048116855, 0.827201315069765, 0.9284348836635735, 0.9862838086968123],\n      [0, 0.20119409399743451, 0.3941513470775634, 0.5709721726085388, 0.7244177313601701, 0.8482065834104272, 0.937273392400706, 0.9879925180204854],\n      [0.09501250983763744, 0.2816035507792589, 0.45801677765722737, 0.6178762444026438, 0.755404408355003, 0.8656312023878318, 0.9445750230732326, 0.9894009349916499]\n    ],\n    weights: [\n      [1],\n      [0.8888888888888888, 0.5555555555555556],\n      [0.6521451548625461, 0.34785484513745385],\n      [0.5688888888888889, 0.47862867049936647, 0.23692688505618908],\n      [0.46791393457269104, 0.3607615730481386, 0.17132449237917036],\n      [0.4179591836734694, 0.3818300505051189, 0.27970539148927664, 0.1294849661688697],\n      [0.362683783378362, 0.31370664587788727, 0.22238103445337448, 0.10122853629037626],\n      [0.3302393550012598, 0.31234707704000286, 0.26061069640293544, 0.1806481606948574, 0.08127438836157441],\n      [0.29552422471475287, 0.26926671930999635, 0.21908636251598204, 0.1494513491505806, 0.06667134430868814],\n      [0.2729250867779006, 0.26280454451024665, 0.23319376459199048, 0.18629021092773426, 0.1255803694649046, 0.05566856711617366],\n      [0.24914704581340277, 0.2334925365383548, 0.20316742672306592, 0.16007832854334622, 0.10693932599531843, 0.04717533638651183],\n      [0.2325515532308739, 0.22628318026289723, 0.2078160475368885, 0.17814598076194574, 0.13887351021978725, 0.09212149983772845, 0.04048400476531588],\n      [0.2152638534631578, 0.2051984637212956, 0.18553839747793782, 0.15720316715819355, 0.12151857068790319, 0.08015808715976021, 0.03511946033175186],\n      [0.2025782419255613, 0.19843148532711158, 0.1861610000155622, 0.16626920581699392, 0.13957067792615432, 0.10715922046717194, 0.07036604748810812, 0.03075324199611727],\n      [0.1894506104550685, 0.18260341504492358, 0.16915651939500254, 0.14959598881657674, 0.12462897125553388, 0.09515851168249279, 0.062253523938647894, 0.027152459411754096]\n    ]\n  };\n  function getComponentOnCubicBezier(t, a, b, c, d) {\n    const k = 1 - t;\n    return k * k * k * a + 3 * k * k * t * b + 3 * k * t * t * c + t * t * t * d;\n  }\n  function subdivide(x1, y1, x2, y2, x3, y3, x4, y4, limit) {\n    limit = limit || Curve.RecursionLimit;\n    const amount = limit + 1;\n    if (Math.abs(x1 - x4) < 1e-3 && Math.abs(y1 - y4) < 1e-3) {\n      return [new Anchor(x4, y4)];\n    }\n    const result = [];\n    for (let i = 0; i < amount; i++) {\n      const t = i / amount;\n      const x = getComponentOnCubicBezier(t, x1, x2, x3, x4);\n      const y = getComponentOnCubicBezier(t, y1, y2, y3, y4);\n      result.push(new Anchor(x, y));\n    }\n    return result;\n  }\n  function getCurveLength(x1, y1, x2, y2, x3, y3, x4, y4, limit) {\n    if (x1 === x2 && y1 === y2 && x3 === x4 && y3 === y4) {\n      const dx = x4 - x1;\n      const dy = y4 - y1;\n      return Math.sqrt(dx * dx + dy * dy);\n    }\n    const ax = 9 * (x2 - x3) + 3 * (x4 - x1), bx = 6 * (x1 + x3) - 12 * x2, cx = 3 * (x2 - x1), ay = 9 * (y2 - y3) + 3 * (y4 - y1), by = 6 * (y1 + y3) - 12 * y2, cy = 3 * (y2 - y1);\n    function integrand(t) {\n      const dx = (ax * t + bx) * t + cx, dy = (ay * t + by) * t + cy;\n      return Math.sqrt(dx * dx + dy * dy);\n    }\n    return integrate(\n      integrand,\n      0,\n      1,\n      limit || Curve.RecursionLimit\n    );\n  }\n  function getCurveBoundingBox(x1, y1, x2, y2, x3, y3, x4, y4) {\n    const tvalues = [];\n    const bounds = [[], []];\n    let a, b, c, t, t1, t2, b2ac, sqrtb2ac;\n    for (let i = 0; i < 2; ++i) {\n      if (i == 0) {\n        b = 6 * x1 - 12 * x2 + 6 * x3;\n        a = -3 * x1 + 9 * x2 - 9 * x3 + 3 * x4;\n        c = 3 * x2 - 3 * x1;\n      } else {\n        b = 6 * y1 - 12 * y2 + 6 * y3;\n        a = -3 * y1 + 9 * y2 - 9 * y3 + 3 * y4;\n        c = 3 * y2 - 3 * y1;\n      }\n      if (Math.abs(a) < 1e-12) {\n        if (Math.abs(b) < 1e-12) {\n          continue;\n        }\n        t = -c / b;\n        if (0 < t && t < 1) {\n          tvalues.push(t);\n        }\n        continue;\n      }\n      b2ac = b * b - 4 * c * a;\n      sqrtb2ac = Math.sqrt(b2ac);\n      if (b2ac < 0) {\n        continue;\n      }\n      t1 = (-b + sqrtb2ac) / (2 * a);\n      if (0 < t1 && t1 < 1) {\n        tvalues.push(t1);\n      }\n      t2 = (-b - sqrtb2ac) / (2 * a);\n      if (0 < t2 && t2 < 1) {\n        tvalues.push(t2);\n      }\n    }\n    let j = tvalues.length;\n    let jlen = j;\n    let mt;\n    while (j--) {\n      t = tvalues[j];\n      mt = 1 - t;\n      bounds[0][j] = mt * mt * mt * x1 + 3 * mt * mt * t * x2 + 3 * mt * t * t * x3 + t * t * t * x4;\n      bounds[1][j] = mt * mt * mt * y1 + 3 * mt * mt * t * y2 + 3 * mt * t * t * y3 + t * t * t * y4;\n    }\n    bounds[0][jlen] = x1;\n    bounds[1][jlen] = y1;\n    bounds[0][jlen + 1] = x4;\n    bounds[1][jlen + 1] = y4;\n    bounds[0].length = bounds[1].length = jlen + 2;\n    return {\n      min: { x: Math.min.apply(0, bounds[0]), y: Math.min.apply(0, bounds[1]) },\n      max: { x: Math.max.apply(0, bounds[0]), y: Math.max.apply(0, bounds[1]) }\n    };\n  }\n  function integrate(f, a, b, n) {\n    let x = Curve.abscissas[n - 2], w = Curve.weights[n - 2], A = 0.5 * (b - a), B = A + a, i = 0, m = n + 1 >> 1, sum = n & 1 ? w[i++] * f(B) : 0;\n    while (i < m) {\n      const Ax = A * x[i];\n      sum += w[i++] * (f(B + Ax) + f(B - Ax));\n    }\n    return A * sum;\n  }\n  function getCurveFromPoints(points, closed2) {\n    const l = points.length, last = l - 1;\n    for (let i = 0; i < l; i++) {\n      const point = points[i];\n      const prev = closed2 ? mod(i - 1, l) : Math.max(i - 1, 0);\n      const next = closed2 ? mod(i + 1, l) : Math.min(i + 1, last);\n      const a = points[prev];\n      const b = point;\n      const c = points[next];\n      getControlPoints(a, b, c);\n      b.command = i === 0 ? Commands.move : Commands.curve;\n    }\n  }\n  function getControlPoints(a, b, c) {\n    const a1 = Vector.angleBetween(a, b);\n    const a2 = Vector.angleBetween(c, b);\n    let d1 = Vector.distanceBetween(a, b);\n    let d2 = Vector.distanceBetween(c, b);\n    let mid = (a1 + a2) / 2;\n    if (d1 < 1e-4 || d2 < 1e-4) {\n      if (typeof b.relative === \"boolean\" && !b.relative) {\n        b.controls.left.copy(b);\n        b.controls.right.copy(b);\n      }\n      return b;\n    }\n    d1 *= 0.33;\n    d2 *= 0.33;\n    if (a2 < a1) {\n      mid += HALF_PI;\n    } else {\n      mid -= HALF_PI;\n    }\n    b.controls.left.x = Math.cos(mid) * d1;\n    b.controls.left.y = Math.sin(mid) * d1;\n    mid -= Math.PI;\n    b.controls.right.x = Math.cos(mid) * d2;\n    b.controls.right.y = Math.sin(mid) * d2;\n    if (typeof b.relative === \"boolean\" && !b.relative) {\n      b.controls.left.x += b.x;\n      b.controls.left.y += b.y;\n      b.controls.right.x += b.x;\n      b.controls.right.y += b.y;\n    }\n    return b;\n  }\n  function getReflection(a, b, relative) {\n    return new Vector(\n      2 * a.x - (b.x + a.x) - (relative ? a.x : 0),\n      2 * a.y - (b.y + a.y) - (relative ? a.y : 0)\n    );\n  }\n  function getAnchorsFromArcData(center, xAxisRotation, rx, ry, ts, td, ccw) {\n    const resolution = Constants.Resolution;\n    const anchors = [];\n    for (let i = 0; i < resolution; i++) {\n      let pct = (i + 1) / resolution;\n      if (ccw) {\n        pct = 1 - pct;\n      }\n      const theta = pct * td + ts;\n      const x = rx * Math.cos(theta);\n      const y = ry * Math.sin(theta);\n      const anchor2 = new Anchor(x, y);\n      anchor2.command = Commands.line;\n      anchors.push(anchor2);\n    }\n  }\n\n  // src/utils/device-pixel-ratio.js\n  var devicePixelRatio = root.devicePixelRatio || 1;\n  function getBackingStoreRatio(ctx) {\n    return ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;\n  }\n  function getRatio(ctx) {\n    return devicePixelRatio / getBackingStoreRatio(ctx);\n  }\n\n  // src/utils/underscore.js\n  var slice = Array.prototype.slice;\n  function isArrayLike(collection) {\n    if (collection === null || collection === void 0)\n      return false;\n    const length = collection.length;\n    return typeof length == \"number\" && length >= 0 && length < 4294967296;\n  }\n  var _ = {\n    isNaN: function(obj) {\n      return typeof obj === \"number\" && obj !== +obj;\n    },\n    isElement: function(obj) {\n      return !!(obj && obj.nodeType === 1);\n    },\n    isObject: function(obj) {\n      const type = typeof obj;\n      return type === \"function\" || type === \"object\" && !!obj;\n    },\n    extend: function(base) {\n      const sources = slice.call(arguments, 1);\n      for (let i = 0; i < sources.length; i++) {\n        const obj = sources[i];\n        for (let k in obj) {\n          base[k] = obj[k];\n        }\n      }\n      return base;\n    },\n    defaults: function(base) {\n      const sources = slice.call(arguments, 1);\n      for (let i = 0; i < sources.length; i++) {\n        const obj = sources[i];\n        for (let k in obj) {\n          if (base[k] === void 0) {\n            base[k] = obj[k];\n          }\n        }\n      }\n      return base;\n    },\n    each: function(obj, iteratee, context) {\n      const ctx = context || this;\n      const keys = !isArrayLike(obj) && Object.keys(obj);\n      const length = (keys || obj).length;\n      for (let i = 0; i < length; i++) {\n        const k = keys ? keys[i] : i;\n        iteratee.call(ctx, obj[k], k, obj);\n      }\n      return obj;\n    },\n    performance: root.performance && root.performance.now ? root.performance : Date\n  };\n\n  // src/element.js\n  var Element = class extends Events {\n    _flagId = false;\n    _flagClassName = false;\n    _renderer = {};\n    _id = \"\";\n    _className = \"\";\n    classList = [];\n    constructor() {\n      super();\n      for (let prop in proto3) {\n        Object.defineProperty(this, prop, proto3[prop]);\n      }\n    }\n    flagReset() {\n      this._flagId = this._flagClassName = false;\n    }\n  };\n  var proto3 = {\n    renderer: {\n      enumerable: false,\n      get: function() {\n        return this._renderer;\n      }\n    },\n    id: {\n      enumerable: true,\n      get: function() {\n        return this._id;\n      },\n      set: function(v) {\n        const id = this._id;\n        if (v === this._id) {\n          return;\n        }\n        this._id = v;\n        this._flagId = true;\n        if (this.parent) {\n          delete this.parent.children.ids[id];\n          this.parent.children.ids[this._id] = this;\n        }\n      }\n    },\n    className: {\n      enumerable: true,\n      get: function() {\n        return this._className;\n      },\n      set: function(v) {\n        if (this._className !== v) {\n          this._flagClassName = true;\n          this.classList = v.split(/\\s+?/);\n          this._className = v;\n        }\n      }\n    }\n  };\n\n  // src/matrix.js\n  var cos = Math.cos;\n  var sin = Math.sin;\n  var tan = Math.tan;\n  var array = [];\n  var _Matrix = class extends Events {\n    elements = new NumArray(9);\n    manual = false;\n    constructor(a, b, c, d, e, f) {\n      super();\n      let elements = a;\n      if (!Array.isArray(elements)) {\n        elements = Array.prototype.slice.call(arguments);\n      }\n      this.identity();\n      if (elements.length > 0) {\n        this.set(elements);\n      }\n    }\n    static Multiply(A, B, C) {\n      if (B.length <= 3) {\n        const e = A;\n        let x, y, z;\n        const a = B[0] || 0, b = B[1] || 0, c = B[2] || 0;\n        x = e[0] * a + e[1] * b + e[2] * c;\n        y = e[3] * a + e[4] * b + e[5] * c;\n        z = e[6] * a + e[7] * b + e[8] * c;\n        return [x, y, z];\n      }\n      const A0 = A[0], A1 = A[1], A2 = A[2];\n      const A3 = A[3], A4 = A[4], A5 = A[5];\n      const A6 = A[6], A7 = A[7], A8 = A[8];\n      const B0 = B[0], B1 = B[1], B2 = B[2];\n      const B3 = B[3], B4 = B[4], B5 = B[5];\n      const B6 = B[6], B7 = B[7], B8 = B[8];\n      C = C || new NumArray(9);\n      C[0] = A0 * B0 + A1 * B3 + A2 * B6;\n      C[1] = A0 * B1 + A1 * B4 + A2 * B7;\n      C[2] = A0 * B2 + A1 * B5 + A2 * B8;\n      C[3] = A3 * B0 + A4 * B3 + A5 * B6;\n      C[4] = A3 * B1 + A4 * B4 + A5 * B7;\n      C[5] = A3 * B2 + A4 * B5 + A5 * B8;\n      C[6] = A6 * B0 + A7 * B3 + A8 * B6;\n      C[7] = A6 * B1 + A7 * B4 + A8 * B7;\n      C[8] = A6 * B2 + A7 * B5 + A8 * B8;\n      return C;\n    }\n    set(a, b, c, d, e, f, g, h, i) {\n      if (typeof b === \"undefined\") {\n        const elements = a;\n        a = elements[0];\n        b = elements[1];\n        c = elements[2];\n        d = elements[3];\n        e = elements[4];\n        f = elements[5];\n        g = elements[6];\n        h = elements[7];\n        i = elements[8];\n      }\n      this.elements[0] = a;\n      this.elements[1] = b;\n      this.elements[2] = c;\n      this.elements[3] = d;\n      this.elements[4] = e;\n      this.elements[5] = f;\n      this.elements[6] = g;\n      this.elements[7] = h;\n      this.elements[8] = i;\n      return this.trigger(Events.Types.change);\n    }\n    copy(m) {\n      this.elements[0] = m.elements[0];\n      this.elements[1] = m.elements[1];\n      this.elements[2] = m.elements[2];\n      this.elements[3] = m.elements[3];\n      this.elements[4] = m.elements[4];\n      this.elements[5] = m.elements[5];\n      this.elements[6] = m.elements[6];\n      this.elements[7] = m.elements[7];\n      this.elements[8] = m.elements[8];\n      this.manual = m.manual;\n      return this.trigger(Events.Types.change);\n    }\n    identity() {\n      this.elements[0] = _Matrix.Identity[0];\n      this.elements[1] = _Matrix.Identity[1];\n      this.elements[2] = _Matrix.Identity[2];\n      this.elements[3] = _Matrix.Identity[3];\n      this.elements[4] = _Matrix.Identity[4];\n      this.elements[5] = _Matrix.Identity[5];\n      this.elements[6] = _Matrix.Identity[6];\n      this.elements[7] = _Matrix.Identity[7];\n      this.elements[8] = _Matrix.Identity[8];\n      return this.trigger(Events.Types.change);\n    }\n    multiply(a, b, c, d, e, f, g, h, i) {\n      if (typeof b === \"undefined\") {\n        this.elements[0] *= a;\n        this.elements[1] *= a;\n        this.elements[2] *= a;\n        this.elements[3] *= a;\n        this.elements[4] *= a;\n        this.elements[5] *= a;\n        this.elements[6] *= a;\n        this.elements[7] *= a;\n        this.elements[8] *= a;\n        return this.trigger(Events.Types.change);\n      }\n      if (typeof c === \"undefined\") {\n        c = 1;\n      }\n      if (typeof d === \"undefined\") {\n        a = a || 0;\n        b = b || 0;\n        c = c || 0;\n        e = this.elements;\n        const x = e[0] * a + e[1] * b + e[2] * c;\n        const y = e[3] * a + e[4] * b + e[5] * c;\n        const z = e[6] * a + e[7] * b + e[8] * c;\n        return [x, y, z];\n      }\n      const A = this.elements;\n      const B = [a, b, c, d, e, f, g, h, i];\n      const A0 = A[0], A1 = A[1], A2 = A[2];\n      const A3 = A[3], A4 = A[4], A5 = A[5];\n      const A6 = A[6], A7 = A[7], A8 = A[8];\n      const B0 = B[0], B1 = B[1], B2 = B[2];\n      const B3 = B[3], B4 = B[4], B5 = B[5];\n      const B6 = B[6], B7 = B[7], B8 = B[8];\n      this.elements[0] = A0 * B0 + A1 * B3 + A2 * B6;\n      this.elements[1] = A0 * B1 + A1 * B4 + A2 * B7;\n      this.elements[2] = A0 * B2 + A1 * B5 + A2 * B8;\n      this.elements[3] = A3 * B0 + A4 * B3 + A5 * B6;\n      this.elements[4] = A3 * B1 + A4 * B4 + A5 * B7;\n      this.elements[5] = A3 * B2 + A4 * B5 + A5 * B8;\n      this.elements[6] = A6 * B0 + A7 * B3 + A8 * B6;\n      this.elements[7] = A6 * B1 + A7 * B4 + A8 * B7;\n      this.elements[8] = A6 * B2 + A7 * B5 + A8 * B8;\n      return this.trigger(Events.Types.change);\n    }\n    inverse(out) {\n      const a = this.elements;\n      out = out || new _Matrix();\n      const a00 = a[0], a01 = a[1], a02 = a[2];\n      const a10 = a[3], a11 = a[4], a12 = a[5];\n      const a20 = a[6], a21 = a[7], a22 = a[8];\n      const b01 = a22 * a11 - a12 * a21;\n      const b11 = -a22 * a10 + a12 * a20;\n      const b21 = a21 * a10 - a11 * a20;\n      let det = a00 * b01 + a01 * b11 + a02 * b21;\n      if (!det) {\n        return null;\n      }\n      det = 1 / det;\n      out.elements[0] = b01 * det;\n      out.elements[1] = (-a22 * a01 + a02 * a21) * det;\n      out.elements[2] = (a12 * a01 - a02 * a11) * det;\n      out.elements[3] = b11 * det;\n      out.elements[4] = (a22 * a00 - a02 * a20) * det;\n      out.elements[5] = (-a12 * a00 + a02 * a10) * det;\n      out.elements[6] = b21 * det;\n      out.elements[7] = (-a21 * a00 + a01 * a20) * det;\n      out.elements[8] = (a11 * a00 - a01 * a10) * det;\n      return out;\n    }\n    scale(sx, sy) {\n      const l = arguments.length;\n      if (l <= 1) {\n        sy = sx;\n      }\n      return this.multiply(sx, 0, 0, 0, sy, 0, 0, 0, 1);\n    }\n    rotate(Number2) {\n      const c = cos(Number2);\n      const s = sin(Number2);\n      return this.multiply(c, -s, 0, s, c, 0, 0, 0, 1);\n    }\n    translate(x, y) {\n      return this.multiply(1, 0, x, 0, 1, y, 0, 0, 1);\n    }\n    skewX(Number2) {\n      const a = tan(Number2);\n      return this.multiply(1, a, 0, 0, 1, 0, 0, 0, 1);\n    }\n    skewY(Number2) {\n      const a = tan(Number2);\n      return this.multiply(1, 0, 0, a, 1, 0, 0, 0, 1);\n    }\n    toString(fullMatrix) {\n      array.length = 0;\n      this.toTransformArray(fullMatrix, array);\n      return array.map(toFixed).join(\" \");\n    }\n    toTransformArray(fullMatrix, output) {\n      const elements = this.elements;\n      const hasOutput = !!output;\n      const a = elements[0];\n      const b = elements[1];\n      const c = elements[2];\n      const d = elements[3];\n      const e = elements[4];\n      const f = elements[5];\n      if (fullMatrix) {\n        const g = elements[6];\n        const h = elements[7];\n        const i = elements[8];\n        if (hasOutput) {\n          output[0] = a;\n          output[1] = d;\n          output[2] = g;\n          output[3] = b;\n          output[4] = e;\n          output[5] = h;\n          output[6] = c;\n          output[7] = f;\n          output[8] = i;\n          return;\n        }\n        return [\n          a,\n          d,\n          g,\n          b,\n          e,\n          h,\n          c,\n          f,\n          i\n        ];\n      }\n      if (hasOutput) {\n        output[0] = a;\n        output[1] = d;\n        output[2] = b;\n        output[3] = e;\n        output[4] = c;\n        output[5] = f;\n        return;\n      }\n      return [\n        a,\n        d,\n        b,\n        e,\n        c,\n        f\n      ];\n    }\n    toArray(fullMatrix, output) {\n      const elements = this.elements;\n      const hasOutput = !!output;\n      const a = elements[0];\n      const b = elements[1];\n      const c = elements[2];\n      const d = elements[3];\n      const e = elements[4];\n      const f = elements[5];\n      if (fullMatrix) {\n        const g = elements[6];\n        const h = elements[7];\n        const i = elements[8];\n        if (hasOutput) {\n          output[0] = a;\n          output[1] = b;\n          output[2] = c;\n          output[3] = d;\n          output[4] = e;\n          output[5] = f;\n          output[6] = g;\n          output[7] = h;\n          output[8] = i;\n          return;\n        }\n        return [\n          a,\n          b,\n          c,\n          d,\n          e,\n          f,\n          g,\n          h,\n          i\n        ];\n      }\n      if (hasOutput) {\n        output[0] = a;\n        output[1] = b;\n        output[2] = c;\n        output[3] = d;\n        output[4] = e;\n        output[5] = f;\n        return;\n      }\n      return [\n        a,\n        b,\n        c,\n        d,\n        e,\n        f\n      ];\n    }\n    toObject() {\n      return {\n        elements: this.toArray(true),\n        manual: !!this.manual\n      };\n    }\n    clone() {\n      return new _Matrix().copy(this);\n    }\n  };\n  var Matrix2 = _Matrix;\n  __publicField(Matrix2, \"Identity\", [\n    1,\n    0,\n    0,\n    0,\n    1,\n    0,\n    0,\n    0,\n    1\n  ]);\n  setMatrix(Matrix2);\n\n  // src/shape.js\n  var Shape = class extends Element {\n    _flagMatrix = true;\n    _flagScale = false;\n    _matrix = null;\n    _worldMatrix = null;\n    _position = null;\n    _rotation = 0;\n    _scale = 1;\n    _skewX = 0;\n    _skewY = 0;\n    constructor() {\n      super();\n      for (let prop in proto4) {\n        Object.defineProperty(this, prop, proto4[prop]);\n      }\n      this._renderer.flagMatrix = FlagMatrix.bind(this);\n      this.isShape = true;\n      this.id = Constants.Identifier + Constants.uniqueId();\n      this.matrix = new Matrix2();\n      this.worldMatrix = new Matrix2();\n      this.position = new Vector();\n      this.rotation = 0;\n      this.scale = 1;\n      this.skewX = 0;\n      this.skewY = 0;\n    }\n    get renderer() {\n      return this._renderer;\n    }\n    set renderer(v) {\n      this._renderer = v;\n    }\n    get translation() {\n      return proto4.position.get.apply(this, arguments);\n    }\n    set translation(v) {\n      proto4.position.set.apply(this, arguments);\n    }\n    addTo(group) {\n      group.add(this);\n      return this;\n    }\n    remove() {\n      if (!this.parent) {\n        return this;\n      }\n      this.parent.remove(this);\n      return this;\n    }\n    clone(parent) {\n      const clone = new Shape();\n      clone.position.copy(this.position);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone._update();\n    }\n    _update(bubbles) {\n      if (!this._matrix.manual && this._flagMatrix) {\n        this._matrix.identity().translate(this.position.x, this.position.y);\n        if (this._scale instanceof Vector) {\n          this._matrix.scale(this._scale.x, this._scale.y);\n        } else {\n          this._matrix.scale(this._scale);\n        }\n        this._matrix.rotate(this.rotation);\n        this._matrix.skewX(this.skewX);\n        this._matrix.skewY(this.skewY);\n      }\n      if (bubbles) {\n        if (this.parent && this.parent._update) {\n          this.parent._update();\n        }\n      }\n      return this;\n    }\n    flagReset() {\n      this._flagMatrix = this._flagScale = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var proto4 = {\n    position: {\n      enumerable: true,\n      get: function() {\n        return this._position;\n      },\n      set: function(v) {\n        if (this._position) {\n          this._position.unbind(Events.Types.change, this._renderer.flagMatrix);\n        }\n        this._position = v;\n        this._position.bind(Events.Types.change, this._renderer.flagMatrix);\n        FlagMatrix.call(this);\n      }\n    },\n    rotation: {\n      enumerable: true,\n      get: function() {\n        return this._rotation;\n      },\n      set: function(v) {\n        this._rotation = v;\n        this._flagMatrix = true;\n      }\n    },\n    scale: {\n      enumerable: true,\n      get: function() {\n        return this._scale;\n      },\n      set: function(v) {\n        if (this._scale instanceof Vector) {\n          this._scale.unbind(Events.Types.change, this._renderer.flagMatrix);\n        }\n        this._scale = v;\n        if (this._scale instanceof Vector) {\n          this._scale.bind(Events.Types.change, this._renderer.flagMatrix);\n        }\n        this._flagMatrix = true;\n        this._flagScale = true;\n      }\n    },\n    skewX: {\n      enumerable: true,\n      get: function() {\n        return this._skewX;\n      },\n      set: function(v) {\n        this._skewX = v;\n        this._flagMatrix = true;\n      }\n    },\n    skewY: {\n      enumerable: true,\n      get: function() {\n        return this._skewY;\n      },\n      set: function(v) {\n        this._skewY = v;\n        this._flagMatrix = true;\n      }\n    },\n    matrix: {\n      enumerable: true,\n      get: function() {\n        return this._matrix;\n      },\n      set: function(v) {\n        this._matrix = v;\n        this._flagMatrix = true;\n      }\n    },\n    worldMatrix: {\n      enumerable: true,\n      get: function() {\n        getComputedMatrix(this, this._worldMatrix);\n        return this._worldMatrix;\n      },\n      set: function(v) {\n        this._worldMatrix = v;\n      }\n    }\n  };\n  function FlagMatrix() {\n    this._flagMatrix = true;\n  }\n\n  // src/collection.js\n  var Collection = class extends Array {\n    _events = new Events();\n    get _bound() {\n      return this._events._bound;\n    }\n    set _bound(v) {\n      this._events._bound = v;\n    }\n    addEventListener() {\n      return this._events.addEventListener.apply(this, arguments);\n    }\n    on() {\n      return this._events.on.apply(this, arguments);\n    }\n    bind() {\n      return this._events.bind.apply(this, arguments);\n    }\n    removeEventListener() {\n      return this._events.removeEventListener.apply(this, arguments);\n    }\n    off() {\n      return this._events.off.apply(this, arguments);\n    }\n    unbind() {\n      return this._events.unbind.apply(this, arguments);\n    }\n    dispatchEvent() {\n      return this._events.dispatchEvent.apply(this, arguments);\n    }\n    trigger() {\n      return this._events.trigger.apply(this, arguments);\n    }\n    listen() {\n      return this._events.listen.apply(this, arguments);\n    }\n    ignore() {\n      return this._events.ignore.apply(this, arguments);\n    }\n    constructor() {\n      super();\n      if (arguments[0] && Array.isArray(arguments[0])) {\n        if (arguments[0].length > 0) {\n          this.push.apply(this, arguments[0]);\n        }\n      } else if (arguments.length > 0) {\n        this.push.apply(this, arguments);\n      }\n    }\n    pop() {\n      const popped = super.pop.apply(this, arguments);\n      this.trigger(Events.Types.remove, [popped]);\n      return popped;\n    }\n    shift() {\n      const shifted = super.shift.apply(this, arguments);\n      this.trigger(Events.Types.remove, [shifted]);\n      return shifted;\n    }\n    push() {\n      const pushed = super.push.apply(this, arguments);\n      this.trigger(Events.Types.insert, arguments);\n      return pushed;\n    }\n    unshift() {\n      const unshifted = super.unshift.apply(this, arguments);\n      this.trigger(Events.Types.insert, arguments);\n      return unshifted;\n    }\n    splice() {\n      const spliced = super.splice.apply(this, arguments);\n      this.trigger(Events.Types.remove, spliced);\n      if (arguments.length > 2) {\n        const inserted = this.slice(arguments[0], arguments[0] + arguments.length - 2);\n        this.trigger(Events.Types.insert, inserted);\n        this.trigger(Events.Types.order);\n      }\n      return spliced;\n    }\n    sort() {\n      super.sort.apply(this, arguments);\n      this.trigger(Events.Types.order);\n      return this;\n    }\n    reverse() {\n      super.reverse.apply(this, arguments);\n      this.trigger(Events.Types.order);\n      return this;\n    }\n    indexOf() {\n      return super.indexOf.apply(this, arguments);\n    }\n    map(func, scope) {\n      const results = [];\n      for (let key = 0; key < this.length; key++) {\n        const value = this[key];\n        let result;\n        if (scope) {\n          result = func.call(scope, value, key);\n        } else {\n          result = func(value, key);\n        }\n        results.push(result);\n      }\n      return results;\n    }\n  };\n\n  // src/children.js\n  var Children = class extends Collection {\n    ids = {};\n    constructor(children) {\n      children = Array.isArray(children) ? children : Array.prototype.slice.call(arguments);\n      super(children);\n      this.attach(children);\n      this.on(Events.Types.insert, this.attach);\n      this.on(Events.Types.remove, this.detach);\n    }\n    attach(children) {\n      for (let i = 0; i < children.length; i++) {\n        const child = children[i];\n        if (child && child.id) {\n          this.ids[child.id] = child;\n        }\n      }\n      return this;\n    }\n    detach(children) {\n      for (let i = 0; i < children.length; i++) {\n        delete this.ids[children[i].id];\n      }\n      return this;\n    }\n  };\n\n  // src/group.js\n  var min = Math.min;\n  var max = Math.max;\n  var _Group = class extends Shape {\n    _flagAdditions = false;\n    _flagSubtractions = false;\n    _flagOrder = false;\n    _flagOpacity = true;\n    _flagBeginning = false;\n    _flagEnding = false;\n    _flagLength = false;\n    _flagMask = false;\n    _fill = \"#fff\";\n    _stroke = \"#000\";\n    _linewidth = 1;\n    _opacity = 1;\n    _visible = true;\n    _cap = \"round\";\n    _join = \"round\";\n    _miter = 4;\n    _closed = true;\n    _curved = false;\n    _automatic = true;\n    _beginning = 0;\n    _ending = 1;\n    _length = 0;\n    _mask = null;\n    constructor(children) {\n      super();\n      for (let prop in proto5) {\n        Object.defineProperty(this, prop, proto5[prop]);\n      }\n      this._renderer.type = \"group\";\n      this.additions = [];\n      this.subtractions = [];\n      this.children = Array.isArray(children) ? children : Array.prototype.slice.call(arguments);\n    }\n    static InsertChildren(children) {\n      for (let i = 0; i < children.length; i++) {\n        replaceParent.call(this, children[i], this);\n      }\n    }\n    static RemoveChildren(children) {\n      for (let i = 0; i < children.length; i++) {\n        replaceParent.call(this, children[i]);\n      }\n    }\n    static OrderChildren(children) {\n      this._flagOrder = true;\n    }\n    clone(parent) {\n      const clone = new _Group();\n      const children = this.children.map(function(child) {\n        return child.clone();\n      });\n      clone.add(children);\n      clone.opacity = this.opacity;\n      if (this.mask) {\n        clone.mask = this.mask;\n      }\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.className = this.className;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone._update();\n    }\n    toObject() {\n      const result = {\n        children: [],\n        translation: this.translation.toObject(),\n        rotation: this.rotation,\n        scale: this.scale instanceof Vector ? this.scale.toObject() : this.scale,\n        opacity: this.opacity,\n        className: this.className,\n        mask: this.mask ? this.mask.toObject() : null\n      };\n      if (this.matrix.manual) {\n        result.matrix = this.matrix.toObject();\n      }\n      _.each(this.children, function(child, i) {\n        result.children[i] = child.toObject();\n      }, this);\n      return result;\n    }\n    corner() {\n      const rect = this.getBoundingClientRect(true);\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        child.translation.x -= rect.left;\n        child.translation.y -= rect.top;\n      }\n      if (this.mask) {\n        this.mask.translation.x -= rect.left;\n        this.mask.translation.y -= rect.top;\n      }\n      return this;\n    }\n    center() {\n      const rect = this.getBoundingClientRect(true);\n      const cx = rect.left + rect.width / 2 - this.translation.x;\n      const cy = rect.top + rect.height / 2 - this.translation.y;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        if (child.isShape) {\n          child.translation.x -= cx;\n          child.translation.y -= cy;\n        }\n      }\n      if (this.mask) {\n        this.mask.translation.x -= cx;\n        this.mask.translation.y -= cy;\n      }\n      return this;\n    }\n    getById(id) {\n      let found = null;\n      function search(node) {\n        if (node.id === id) {\n          return node;\n        } else if (node.children) {\n          for (let i = 0; i < node.children.length; i++) {\n            found = search(node.children[i]);\n            if (found) {\n              return found;\n            }\n          }\n        }\n        return null;\n      }\n      return search(this);\n    }\n    getByClassName(className) {\n      const found = [];\n      function search(node) {\n        if (Array.prototype.indexOf.call(node.classList, className) >= 0) {\n          found.push(node);\n        }\n        if (node.children) {\n          for (let i = 0; i < node.children.length; i++) {\n            const child = node.children[i];\n            search(child);\n          }\n        }\n        return found;\n      }\n      return search(this);\n    }\n    getByType(type) {\n      const found = [];\n      function search(node) {\n        if (node instanceof type) {\n          found.push(node);\n        }\n        if (node.children) {\n          for (let i = 0; i < node.children.length; i++) {\n            const child = node.children[i];\n            search(child);\n          }\n        }\n        return found;\n      }\n      return search(this);\n    }\n    add(objects) {\n      if (!(objects instanceof Array)) {\n        objects = Array.prototype.slice.call(arguments);\n      } else {\n        objects = objects.slice();\n      }\n      for (let i = 0; i < objects.length; i++) {\n        const child = objects[i];\n        if (!(child && child.id)) {\n          continue;\n        }\n        const index = Array.prototype.indexOf.call(this.children, child);\n        if (index >= 0) {\n          this.children.splice(index, 1);\n        }\n        this.children.push(child);\n      }\n      return this;\n    }\n    remove(objects) {\n      const l = arguments.length, grandparent = this.parent;\n      if (l <= 0 && grandparent) {\n        grandparent.remove(this);\n        return this;\n      }\n      if (!(objects instanceof Array)) {\n        objects = Array.prototype.slice.call(arguments);\n      } else {\n        objects = objects.slice();\n      }\n      for (let i = 0; i < objects.length; i++) {\n        const object = objects[i];\n        if (!object || !this.children.ids[object.id]) {\n          continue;\n        }\n        const index = this.children.indexOf(object);\n        if (index >= 0) {\n          this.children.splice(index, 1);\n        }\n      }\n      return this;\n    }\n    getBoundingClientRect(shallow) {\n      let rect, matrix, tc, lc, rc, bc;\n      this._update(true);\n      let left = Infinity, right = -Infinity, top = Infinity, bottom = -Infinity;\n      const regex3 = /texture|gradient/i;\n      matrix = shallow ? this.matrix : this.worldMatrix;\n      for (let i = 0; i < this.children.length; i++) {\n        const child = this.children[i];\n        if (!child.visible || regex3.test(child._renderer.type)) {\n          continue;\n        }\n        rect = child.getBoundingClientRect(shallow);\n        tc = typeof rect.top !== \"number\" || _.isNaN(rect.top) || !isFinite(rect.top);\n        lc = typeof rect.left !== \"number\" || _.isNaN(rect.left) || !isFinite(rect.left);\n        rc = typeof rect.right !== \"number\" || _.isNaN(rect.right) || !isFinite(rect.right);\n        bc = typeof rect.bottom !== \"number\" || _.isNaN(rect.bottom) || !isFinite(rect.bottom);\n        if (tc || lc || rc || bc) {\n          continue;\n        }\n        if (shallow) {\n          const [ax, ay] = matrix.multiply(rect.left, rect.top);\n          const [bx, by] = matrix.multiply(rect.right, rect.top);\n          const [cx, cy] = matrix.multiply(rect.left, rect.bottom);\n          const [dx, dy] = matrix.multiply(rect.right, rect.bottom);\n          top = min(ay, by, cy, dy);\n          left = min(ax, bx, cx, dx);\n          right = max(ax, bx, cx, dx);\n          bottom = max(ay, by, cy, dy);\n        } else {\n          top = min(rect.top, top);\n          left = min(rect.left, left);\n          right = max(rect.right, right);\n          bottom = max(rect.bottom, bottom);\n        }\n      }\n      return {\n        top,\n        left,\n        right,\n        bottom,\n        width: right - left,\n        height: bottom - top\n      };\n    }\n    noFill() {\n      this.children.forEach(function(child) {\n        child.noFill();\n      });\n      return this;\n    }\n    noStroke() {\n      this.children.forEach(function(child) {\n        child.noStroke();\n      });\n      return this;\n    }\n    subdivide() {\n      const args = arguments;\n      this.children.forEach(function(child) {\n        child.subdivide.apply(child, args);\n      });\n      return this;\n    }\n    _update() {\n      let i, l, child;\n      if (this._flagBeginning || this._flagEnding) {\n        const beginning = Math.min(this._beginning, this._ending);\n        const ending = Math.max(this._beginning, this._ending);\n        const length = this.length;\n        let sum = 0;\n        const bd = beginning * length;\n        const ed = ending * length;\n        for (i = 0; i < this.children.length; i++) {\n          child = this.children[i];\n          l = child.length;\n          if (bd > sum + l) {\n            child.beginning = 1;\n            child.ending = 1;\n          } else if (ed < sum) {\n            child.beginning = 0;\n            child.ending = 0;\n          } else if (bd > sum && bd < sum + l) {\n            child.beginning = (bd - sum) / l;\n            child.ending = 1;\n          } else if (ed > sum && ed < sum + l) {\n            child.beginning = 0;\n            child.ending = (ed - sum) / l;\n          } else {\n            child.beginning = 0;\n            child.ending = 1;\n          }\n          sum += l;\n        }\n      }\n      return super._update.apply(this, arguments);\n    }\n    flagReset() {\n      if (this._flagAdditions) {\n        this.additions.length = 0;\n        this._flagAdditions = false;\n      }\n      if (this._flagSubtractions) {\n        this.subtractions.length = 0;\n        this._flagSubtractions = false;\n      }\n      this._flagOrder = this._flagMask = this._flagOpacity = this._flagBeginning = this._flagEnding = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var Group = _Group;\n  __publicField(Group, \"Children\", Children);\n  __publicField(Group, \"Properties\", [\n    \"fill\",\n    \"stroke\",\n    \"linewidth\",\n    \"cap\",\n    \"join\",\n    \"miter\",\n    \"closed\",\n    \"curved\",\n    \"automatic\"\n  ]);\n  var proto5 = {\n    visible: {\n      enumerable: true,\n      get: function() {\n        return this._visible;\n      },\n      set: function(v) {\n        this._flagVisible = this._visible !== v || this._flagVisible;\n        this._visible = v;\n      }\n    },\n    opacity: {\n      enumerable: true,\n      get: function() {\n        return this._opacity;\n      },\n      set: function(v) {\n        this._flagOpacity = this._opacity !== v || this._flagOpacity;\n        this._opacity = v;\n      }\n    },\n    beginning: {\n      enumerable: true,\n      get: function() {\n        return this._beginning;\n      },\n      set: function(v) {\n        this._flagBeginning = this._beginning !== v || this._flagBeginning;\n        this._beginning = v;\n      }\n    },\n    ending: {\n      enumerable: true,\n      get: function() {\n        return this._ending;\n      },\n      set: function(v) {\n        this._flagEnding = this._ending !== v || this._flagEnding;\n        this._ending = v;\n      }\n    },\n    length: {\n      enumerable: true,\n      get: function() {\n        if (this._flagLength || this._length <= 0) {\n          this._length = 0;\n          if (!this.children) {\n            return this._length;\n          }\n          for (let i = 0; i < this.children.length; i++) {\n            const child = this.children[i];\n            this._length += child.length;\n          }\n        }\n        return this._length;\n      }\n    },\n    fill: {\n      enumerable: true,\n      get: function() {\n        return this._fill;\n      },\n      set: function(v) {\n        this._fill = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.fill = v;\n        }\n      }\n    },\n    stroke: {\n      enumerable: true,\n      get: function() {\n        return this._stroke;\n      },\n      set: function(v) {\n        this._stroke = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.stroke = v;\n        }\n      }\n    },\n    linewidth: {\n      enumerable: true,\n      get: function() {\n        return this._linewidth;\n      },\n      set: function(v) {\n        this._linewidth = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.linewidth = v;\n        }\n      }\n    },\n    join: {\n      enumerable: true,\n      get: function() {\n        return this._join;\n      },\n      set: function(v) {\n        this._join = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.join = v;\n        }\n      }\n    },\n    miter: {\n      enumerable: true,\n      get: function() {\n        return this._miter;\n      },\n      set: function(v) {\n        this._miter = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.miter = v;\n        }\n      }\n    },\n    cap: {\n      enumerable: true,\n      get: function() {\n        return this._cap;\n      },\n      set: function(v) {\n        this._cap = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.cap = v;\n        }\n      }\n    },\n    closed: {\n      enumerable: true,\n      get: function() {\n        return this._closed;\n      },\n      set: function(v) {\n        this._closed = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.closed = v;\n        }\n      }\n    },\n    curved: {\n      enumerable: true,\n      get: function() {\n        return this._curved;\n      },\n      set: function(v) {\n        this._curved = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.curved = v;\n        }\n      }\n    },\n    automatic: {\n      enumerable: true,\n      get: function() {\n        return this._automatic;\n      },\n      set: function(v) {\n        this._automatic = v;\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          child.automatic = v;\n        }\n      }\n    },\n    children: {\n      enumerable: true,\n      get: function() {\n        return this._children;\n      },\n      set: function(children) {\n        const insertChildren = Group.InsertChildren.bind(this);\n        const removeChildren = Group.RemoveChildren.bind(this);\n        const orderChildren = Group.OrderChildren.bind(this);\n        if (this._children) {\n          this._children.unbind();\n          if (this._children.length > 0) {\n            removeChildren(this._children);\n          }\n        }\n        this._children = new Children(children);\n        this._children.bind(Events.Types.insert, insertChildren);\n        this._children.bind(Events.Types.remove, removeChildren);\n        this._children.bind(Events.Types.order, orderChildren);\n        if (children.length > 0) {\n          insertChildren(children);\n        }\n      }\n    },\n    mask: {\n      enumerable: true,\n      get: function() {\n        return this._mask;\n      },\n      set: function(v) {\n        this._mask = v;\n        this._flagMask = true;\n        if (_.isObject(v) && !v.clip) {\n          v.clip = true;\n        }\n      }\n    }\n  };\n  function replaceParent(child, newParent) {\n    const parent = child.parent;\n    let index;\n    if (parent === newParent) {\n      add();\n      return;\n    }\n    if (parent && parent.children.ids[child.id]) {\n      index = Array.prototype.indexOf.call(parent.children, child);\n      parent.children.splice(index, 1);\n      splice();\n    }\n    if (newParent) {\n      add();\n      return;\n    }\n    splice();\n    if (parent._flagAdditions && parent.additions.length === 0) {\n      parent._flagAdditions = false;\n    }\n    if (parent._flagSubtractions && parent.subtractions.length === 0) {\n      parent._flagSubtractions = false;\n    }\n    delete child.parent;\n    function add() {\n      if (newParent.subtractions.length > 0) {\n        index = Array.prototype.indexOf.call(newParent.subtractions, child);\n        if (index >= 0) {\n          newParent.subtractions.splice(index, 1);\n        }\n      }\n      if (newParent.additions.length > 0) {\n        index = Array.prototype.indexOf.call(newParent.additions, child);\n        if (index >= 0) {\n          newParent.additions.splice(index, 1);\n        }\n      }\n      child.parent = newParent;\n      newParent.additions.push(child);\n      newParent._flagAdditions = true;\n    }\n    function splice() {\n      index = Array.prototype.indexOf.call(parent.additions, child);\n      if (index >= 0) {\n        parent.additions.splice(index, 1);\n      }\n      index = Array.prototype.indexOf.call(parent.subtractions, child);\n      if (index < 0) {\n        parent.subtractions.push(child);\n        parent._flagSubtractions = true;\n      }\n    }\n  }\n\n  // src/renderers/canvas.js\n  var emptyArray = [];\n  var max2 = Math.max;\n  var min2 = Math.min;\n  var abs = Math.abs;\n  var sin2 = Math.sin;\n  var cos2 = Math.cos;\n  var acos = Math.acos;\n  var sqrt = Math.sqrt;\n  var canvas = {\n    isHidden: /(undefined|none|transparent)/i,\n    alignments: {\n      left: \"start\",\n      middle: \"center\",\n      right: \"end\"\n    },\n    shim: function(elem, name) {\n      elem.tagName = elem.nodeName = name || \"canvas\";\n      elem.nodeType = 1;\n      elem.getAttribute = function(prop) {\n        return this[prop];\n      };\n      elem.setAttribute = function(prop, val) {\n        this[prop] = val;\n        return this;\n      };\n      return elem;\n    },\n    group: {\n      renderChild: function(child) {\n        canvas[child._renderer.type].render.call(child, this.ctx, true, this.clip);\n      },\n      render: function(ctx) {\n        if (!this._visible) {\n          return this;\n        }\n        this._update();\n        const matrix = this._matrix.elements;\n        const parent = this.parent;\n        this._renderer.opacity = this._opacity * (parent && parent._renderer ? parent._renderer.opacity : 1);\n        const mask = this._mask;\n        const defaultMatrix = isDefaultMatrix(matrix);\n        const shouldIsolate = !defaultMatrix || !!mask;\n        if (!this._renderer.context) {\n          this._renderer.context = {};\n        }\n        this._renderer.context.ctx = ctx;\n        if (shouldIsolate) {\n          ctx.save();\n          if (!defaultMatrix) {\n            ctx.transform(\n              matrix[0],\n              matrix[3],\n              matrix[1],\n              matrix[4],\n              matrix[2],\n              matrix[5]\n            );\n          }\n        }\n        if (mask) {\n          canvas[mask._renderer.type].render.call(mask, ctx, true);\n        }\n        if (this._opacity > 0 && this._scale !== 0) {\n          for (let i = 0; i < this.children.length; i++) {\n            const child = this.children[i];\n            canvas[child._renderer.type].render.call(child, ctx);\n          }\n        }\n        if (shouldIsolate) {\n          ctx.restore();\n        }\n        return this.flagReset();\n      }\n    },\n    path: {\n      render: function(ctx, forced, parentClipped) {\n        let matrix, stroke, linewidth, fill, opacity, visible, cap, join, miter, closed2, commands, length, last, prev, a, b, c, d, ux, uy, vx, vy, ar, bl, br, cl, x, y, mask, clip, defaultMatrix, isOffset, dashes, po;\n        po = this.parent && this.parent._renderer ? this.parent._renderer.opacity : 1;\n        mask = this._mask;\n        clip = this._clip;\n        opacity = this._opacity * (po || 1);\n        visible = this._visible;\n        if (!forced && (!visible || clip || opacity === 0)) {\n          return this;\n        }\n        this._update();\n        matrix = this._matrix.elements;\n        stroke = this._stroke;\n        linewidth = this._linewidth;\n        fill = this._fill;\n        cap = this._cap;\n        join = this._join;\n        miter = this._miter;\n        closed2 = this._closed;\n        commands = this._renderer.vertices;\n        length = commands.length;\n        last = length - 1;\n        defaultMatrix = isDefaultMatrix(matrix);\n        dashes = this.dashes;\n        if (!defaultMatrix) {\n          ctx.save();\n          ctx.transform(matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5]);\n        }\n        if (mask) {\n          canvas[mask._renderer.type].render.call(mask, ctx, true);\n        }\n        if (fill) {\n          if (typeof fill === \"string\") {\n            ctx.fillStyle = fill;\n          } else {\n            canvas[fill._renderer.type].render.call(fill, ctx, this);\n            ctx.fillStyle = fill._renderer.effect;\n          }\n        }\n        if (stroke) {\n          if (typeof stroke === \"string\") {\n            ctx.strokeStyle = stroke;\n          } else {\n            canvas[stroke._renderer.type].render.call(stroke, ctx, this);\n            ctx.strokeStyle = stroke._renderer.effect;\n          }\n          if (linewidth) {\n            ctx.lineWidth = linewidth;\n          }\n          if (miter) {\n            ctx.miterLimit = miter;\n          }\n          if (join) {\n            ctx.lineJoin = join;\n          }\n          if (!closed2 && cap) {\n            ctx.lineCap = cap;\n          }\n        }\n        if (typeof opacity === \"number\") {\n          ctx.globalAlpha = opacity;\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.lineDashOffset = dashes.offset || 0;\n          ctx.setLineDash(dashes);\n        }\n        ctx.beginPath();\n        let rx, ry, xAxisRotation, largeArcFlag, sweepFlag, ax, ay;\n        for (let i = 0; i < length; i++) {\n          b = commands[i];\n          x = b.x;\n          y = b.y;\n          switch (b.command) {\n            case Commands.close:\n              ctx.closePath();\n              break;\n            case Commands.arc:\n              rx = b.rx;\n              ry = b.ry;\n              xAxisRotation = b.xAxisRotation;\n              largeArcFlag = b.largeArcFlag;\n              sweepFlag = b.sweepFlag;\n              prev = closed2 ? mod(i - 1, length) : max2(i - 1, 0);\n              a = commands[prev];\n              ax = a.x;\n              ay = a.y;\n              canvas.renderSvgArcCommand(\n                ctx,\n                ax,\n                ay,\n                rx,\n                ry,\n                largeArcFlag,\n                sweepFlag,\n                xAxisRotation,\n                x,\n                y\n              );\n              break;\n            case Commands.curve:\n              prev = closed2 ? mod(i - 1, length) : Math.max(i - 1, 0);\n              a = commands[prev];\n              ar = a.controls && a.controls.right || Vector.zero;\n              bl = b.controls && b.controls.left || Vector.zero;\n              if (a._relative) {\n                vx = ar.x + a.x;\n                vy = ar.y + a.y;\n              } else {\n                vx = ar.x;\n                vy = ar.y;\n              }\n              if (b._relative) {\n                ux = bl.x + b.x;\n                uy = bl.y + b.y;\n              } else {\n                ux = bl.x;\n                uy = bl.y;\n              }\n              ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n              if (i >= last && closed2) {\n                c = d;\n                br = b.controls && b.controls.right || Vector.zero;\n                cl = c.controls && c.controls.left || Vector.zero;\n                if (b._relative) {\n                  vx = br.x + b.x;\n                  vy = br.y + b.y;\n                } else {\n                  vx = br.x;\n                  vy = br.y;\n                }\n                if (c._relative) {\n                  ux = cl.x + c.x;\n                  uy = cl.y + c.y;\n                } else {\n                  ux = cl.x;\n                  uy = cl.y;\n                }\n                x = c.x;\n                y = c.y;\n                ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n              }\n              break;\n            case Commands.line:\n              ctx.lineTo(x, y);\n              break;\n            case Commands.move:\n              d = b;\n              ctx.moveTo(x, y);\n              break;\n          }\n        }\n        if (closed2) {\n          ctx.closePath();\n        }\n        if (!clip && !parentClipped) {\n          if (!canvas.isHidden.test(fill)) {\n            isOffset = fill._renderer && fill._renderer.offset;\n            if (isOffset) {\n              ctx.save();\n              ctx.translate(\n                -fill._renderer.offset.x,\n                -fill._renderer.offset.y\n              );\n              ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n            }\n            ctx.fill();\n            if (isOffset) {\n              ctx.restore();\n            }\n          }\n          if (!canvas.isHidden.test(stroke)) {\n            isOffset = stroke._renderer && stroke._renderer.offset;\n            if (isOffset) {\n              ctx.save();\n              ctx.translate(\n                -stroke._renderer.offset.x,\n                -stroke._renderer.offset.y\n              );\n              ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n              ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n            }\n            ctx.stroke();\n            if (isOffset) {\n              ctx.restore();\n            }\n          }\n        }\n        if (!defaultMatrix) {\n          ctx.restore();\n        }\n        if (clip && !parentClipped) {\n          ctx.clip();\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.setLineDash(emptyArray);\n        }\n        return this.flagReset();\n      }\n    },\n    points: {\n      render: function(ctx, forced, parentClipped) {\n        let me, stroke, linewidth, fill, opacity, visible, size, commands, length, b, x, y, defaultMatrix, isOffset, dashes, po;\n        po = this.parent && this.parent._renderer ? this.parent._renderer.opacity : 1;\n        opacity = this._opacity * (po || 1);\n        visible = this._visible;\n        if (!forced && (!visible || opacity === 0)) {\n          return this;\n        }\n        this._update();\n        me = this._matrix.elements;\n        stroke = this._stroke;\n        linewidth = this._linewidth;\n        fill = this._fill;\n        commands = this._renderer.collection;\n        length = commands.length;\n        defaultMatrix = isDefaultMatrix(me);\n        dashes = this.dashes;\n        size = this._size;\n        if (!defaultMatrix) {\n          ctx.save();\n          ctx.transform(me[0], me[3], me[1], me[4], me[2], me[5]);\n        }\n        if (fill) {\n          if (typeof fill === \"string\") {\n            ctx.fillStyle = fill;\n          } else {\n            canvas[fill._renderer.type].render.call(fill, ctx, this);\n            ctx.fillStyle = fill._renderer.effect;\n          }\n        }\n        if (stroke) {\n          if (typeof stroke === \"string\") {\n            ctx.strokeStyle = stroke;\n          } else {\n            canvas[stroke._renderer.type].render.call(stroke, ctx, this);\n            ctx.strokeStyle = stroke._renderer.effect;\n          }\n          if (linewidth) {\n            ctx.lineWidth = linewidth;\n          }\n        }\n        if (typeof opacity === \"number\") {\n          ctx.globalAlpha = opacity;\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.lineDashOffset = dashes.offset || 0;\n          ctx.setLineDash(dashes);\n        }\n        ctx.beginPath();\n        let radius = size * 0.5, m;\n        if (!this._sizeAttenuation) {\n          m = this.worldMatrix.elements;\n          m = decomposeMatrix(m[0], m[3], m[1], m[4], m[2], m[5]);\n          radius /= Math.max(m.scaleX, m.scaleY);\n        }\n        for (let i = 0; i < length; i++) {\n          b = commands[i];\n          x = b.x;\n          y = b.y;\n          ctx.moveTo(x + radius, y);\n          ctx.arc(x, y, radius, 0, TWO_PI);\n        }\n        if (!parentClipped) {\n          if (!canvas.isHidden.test(fill)) {\n            isOffset = fill._renderer && fill._renderer.offset;\n            if (isOffset) {\n              ctx.save();\n              ctx.translate(\n                -fill._renderer.offset.x,\n                -fill._renderer.offset.y\n              );\n              ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n            }\n            ctx.fill();\n            if (isOffset) {\n              ctx.restore();\n            }\n          }\n          if (!canvas.isHidden.test(stroke)) {\n            isOffset = stroke._renderer && stroke._renderer.offset;\n            if (isOffset) {\n              ctx.save();\n              ctx.translate(\n                -stroke._renderer.offset.x,\n                -stroke._renderer.offset.y\n              );\n              ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n              ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n            }\n            ctx.stroke();\n            if (isOffset) {\n              ctx.restore();\n            }\n          }\n        }\n        if (!defaultMatrix) {\n          ctx.restore();\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.setLineDash(emptyArray);\n        }\n        return this.flagReset();\n      }\n    },\n    text: {\n      render: function(ctx, forced, parentClipped) {\n        const po = this.parent && this.parent._renderer ? this.parent._renderer.opacity : 1;\n        const opacity = this._opacity * po;\n        const visible = this._visible;\n        const mask = this._mask;\n        const clip = this._clip;\n        if (!forced && (!visible || clip || opacity === 0)) {\n          return this;\n        }\n        this._update();\n        const matrix = this._matrix.elements;\n        const stroke = this._stroke;\n        const linewidth = this._linewidth;\n        const fill = this._fill;\n        const decoration = this._decoration;\n        const defaultMatrix = isDefaultMatrix(matrix);\n        const isOffset = fill._renderer && fill._renderer.offset && stroke._renderer && stroke._renderer.offset;\n        const dashes = this.dashes;\n        const alignment = canvas.alignments[this._alignment] || this._alignment;\n        const baseline = this._baseline;\n        let a, b, c, d, e, sx, sy, x1, y1, x2, y2;\n        if (!defaultMatrix) {\n          ctx.save();\n          ctx.transform(matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5]);\n        }\n        if (mask) {\n          canvas[mask._renderer.type].render.call(mask, ctx, true);\n        }\n        if (!isOffset) {\n          ctx.font = [this._style, this._weight, this._size + \"px/\" + this._leading + \"px\", this._family].join(\" \");\n        }\n        ctx.textAlign = alignment;\n        ctx.textBaseline = baseline;\n        if (fill) {\n          if (typeof fill === \"string\") {\n            ctx.fillStyle = fill;\n          } else {\n            canvas[fill._renderer.type].render.call(fill, ctx, this);\n            ctx.fillStyle = fill._renderer.effect;\n          }\n        }\n        if (stroke) {\n          if (typeof stroke === \"string\") {\n            ctx.strokeStyle = stroke;\n          } else {\n            canvas[stroke._renderer.type].render.call(stroke, ctx, this);\n            ctx.strokeStyle = stroke._renderer.effect;\n          }\n          if (linewidth) {\n            ctx.lineWidth = linewidth;\n          }\n        }\n        if (typeof opacity === \"number\") {\n          ctx.globalAlpha = opacity;\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.lineDashOffset = dashes.offset || 0;\n          ctx.setLineDash(dashes);\n        }\n        if (!clip && !parentClipped) {\n          if (!canvas.isHidden.test(fill)) {\n            if (fill._renderer && fill._renderer.offset) {\n              sx = fill._renderer.scale.x;\n              sy = fill._renderer.scale.y;\n              ctx.save();\n              ctx.translate(\n                -fill._renderer.offset.x,\n                -fill._renderer.offset.y\n              );\n              ctx.scale(sx, sy);\n              a = this._size / fill._renderer.scale.y;\n              b = this._leading / fill._renderer.scale.y;\n              ctx.font = [\n                this._style,\n                this._weight,\n                a + \"px/\",\n                b + \"px\",\n                this._family\n              ].join(\" \");\n              c = fill._renderer.offset.x / fill._renderer.scale.x;\n              d = fill._renderer.offset.y / fill._renderer.scale.y;\n              ctx.fillText(this.value, c, d);\n              ctx.restore();\n            } else {\n              ctx.fillText(this.value, 0, 0);\n            }\n          }\n          if (!canvas.isHidden.test(stroke)) {\n            if (stroke._renderer && stroke._renderer.offset) {\n              sx = stroke._renderer.scale.x;\n              sy = stroke._renderer.scale.y;\n              ctx.save();\n              ctx.translate(\n                -stroke._renderer.offset.x,\n                -stroke._renderer.offset.y\n              );\n              ctx.scale(sx, sy);\n              a = this._size / stroke._renderer.scale.y;\n              b = this._leading / stroke._renderer.scale.y;\n              ctx.font = [\n                this._style,\n                this._weight,\n                a + \"px/\",\n                b + \"px\",\n                this._family\n              ].join(\" \");\n              c = stroke._renderer.offset.x / stroke._renderer.scale.x;\n              d = stroke._renderer.offset.y / stroke._renderer.scale.y;\n              e = linewidth / stroke._renderer.scale.x;\n              ctx.lineWidth = e;\n              ctx.strokeText(this.value, c, d);\n              ctx.restore();\n            } else {\n              ctx.strokeText(this.value, 0, 0);\n            }\n          }\n        }\n        if (/(underline|strikethrough)/i.test(decoration)) {\n          const metrics = ctx.measureText(this.value);\n          let scalar = 1;\n          switch (decoration) {\n            case \"underline\":\n              y1 = metrics.actualBoundingBoxAscent;\n              y2 = metrics.actualBoundingBoxAscent;\n              break;\n            case \"strikethrough\":\n              y1 = 0;\n              y2 = 0;\n              scalar = 0.5;\n              break;\n          }\n          switch (baseline) {\n            case \"top\":\n              y1 += this._size * scalar;\n              y2 += this._size * scalar;\n              break;\n            case \"baseline\":\n            case \"bottom\":\n              y1 -= this._size * scalar;\n              y2 -= this._size * scalar;\n              break;\n          }\n          switch (alignment) {\n            case \"left\":\n            case \"start\":\n              x1 = 0;\n              x2 = metrics.width;\n              break;\n            case \"right\":\n            case \"end\":\n              x1 = -metrics.width;\n              x2 = 0;\n              break;\n            default:\n              x1 = -metrics.width / 2;\n              x2 = metrics.width / 2;\n          }\n          ctx.lineWidth = Math.max(Math.floor(this._size / 15), 1);\n          ctx.strokeStyle = ctx.fillStyle;\n          ctx.beginPath();\n          ctx.moveTo(x1, y1);\n          ctx.lineTo(x2, y2);\n          ctx.stroke();\n        }\n        if (!defaultMatrix) {\n          ctx.restore();\n        }\n        if (clip && !parentClipped) {\n          ctx.clip();\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.setLineDash(emptyArray);\n        }\n        return this.flagReset();\n      }\n    },\n    \"linear-gradient\": {\n      render: function(ctx, parent) {\n        if (!parent) {\n          return;\n        }\n        this._update();\n        if (!this._renderer.effect || this._flagEndPoints || this._flagStops || this._flagUnits) {\n          let rect;\n          let lx = this.left._x;\n          let ly = this.left._y;\n          let rx = this.right._x;\n          let ry = this.right._y;\n          if (/objectBoundingBox/i.test(this._units)) {\n            rect = parent.getBoundingClientRect(true);\n            lx = (lx - 0.5) * rect.width;\n            ly = (ly - 0.5) * rect.height;\n            rx = (rx - 0.5) * rect.width;\n            ry = (ry - 0.5) * rect.height;\n          }\n          this._renderer.effect = ctx.createLinearGradient(lx, ly, rx, ry);\n          for (let i = 0; i < this.stops.length; i++) {\n            const stop = this.stops[i];\n            this._renderer.effect.addColorStop(stop._offset, stop._color);\n          }\n        }\n        return this.flagReset();\n      }\n    },\n    \"radial-gradient\": {\n      render: function(ctx, parent) {\n        if (!parent) {\n          return;\n        }\n        this._update();\n        if (!this._renderer.effect || this._flagCenter || this._flagFocal || this._flagRadius || this._flagStops || this._flagUnits) {\n          let rect;\n          let cx = this.center._x;\n          let cy = this.center._y;\n          let fx = this.focal._x;\n          let fy = this.focal._y;\n          let radius = this._radius;\n          if (/objectBoundingBox/i.test(this._units)) {\n            rect = parent.getBoundingClientRect(true);\n            cx = cx * rect.width * 0.5;\n            cy = cy * rect.height * 0.5;\n            fx = fx * rect.width * 0.5;\n            fy = fy * rect.height * 0.5;\n            radius *= Math.min(rect.width, rect.height) * 0.5;\n          }\n          this._renderer.effect = ctx.createRadialGradient(\n            cx,\n            cy,\n            0,\n            fx,\n            fy,\n            radius\n          );\n          for (let i = 0; i < this.stops.length; i++) {\n            const stop = this.stops[i];\n            this._renderer.effect.addColorStop(stop._offset, stop._color);\n          }\n        }\n        return this.flagReset();\n      }\n    },\n    texture: {\n      render: function(ctx) {\n        this._update();\n        const image = this.image;\n        if (!this._renderer.effect || (this._flagLoaded || this._flagImage || this._flagVideo || this._flagRepeat) && this.loaded) {\n          this._renderer.effect = ctx.createPattern(this.image, this._repeat);\n        }\n        if (this._flagOffset || this._flagLoaded || this._flagScale) {\n          if (!(this._renderer.offset instanceof Vector)) {\n            this._renderer.offset = new Vector();\n          }\n          this._renderer.offset.x = -this._offset.x;\n          this._renderer.offset.y = -this._offset.y;\n          if (image) {\n            this._renderer.offset.x += image.width / 2;\n            this._renderer.offset.y += image.height / 2;\n            if (this._scale instanceof Vector) {\n              this._renderer.offset.x *= this._scale.x;\n              this._renderer.offset.y *= this._scale.y;\n            } else {\n              this._renderer.offset.x *= this._scale;\n              this._renderer.offset.y *= this._scale;\n            }\n          }\n        }\n        if (this._flagScale || this._flagLoaded) {\n          if (!(this._renderer.scale instanceof Vector)) {\n            this._renderer.scale = new Vector();\n          }\n          if (this._scale instanceof Vector) {\n            this._renderer.scale.copy(this._scale);\n          } else {\n            this._renderer.scale.set(this._scale, this._scale);\n          }\n        }\n        return this.flagReset();\n      }\n    },\n    renderSvgArcCommand: function(ctx, ax, ay, rx, ry, largeArcFlag, sweepFlag, xAxisRotation, x, y) {\n      xAxisRotation = xAxisRotation * Math.PI / 180;\n      rx = abs(rx);\n      ry = abs(ry);\n      const dx2 = (ax - x) / 2;\n      const dy2 = (ay - y) / 2;\n      const x1p = cos2(xAxisRotation) * dx2 + sin2(xAxisRotation) * dy2;\n      const y1p = -sin2(xAxisRotation) * dx2 + cos2(xAxisRotation) * dy2;\n      const x1ps = x1p * x1p;\n      const y1ps = y1p * y1p;\n      let rxs = rx * rx;\n      let rys = ry * ry;\n      const cr = x1ps / rxs + y1ps / rys;\n      if (cr > 1) {\n        const s = sqrt(cr);\n        rx = s * rx;\n        ry = s * ry;\n        rxs = rx * rx;\n        rys = ry * ry;\n      }\n      const dq = rxs * y1ps + rys * x1ps;\n      const pq = (rxs * rys - dq) / dq;\n      let q = sqrt(max2(0, pq));\n      if (largeArcFlag === sweepFlag)\n        q = -q;\n      const cxp = q * rx * y1p / ry;\n      const cyp = -q * ry * x1p / rx;\n      const cx = cos2(xAxisRotation) * cxp - sin2(xAxisRotation) * cyp + (ax + x) / 2;\n      const cy = sin2(xAxisRotation) * cxp + cos2(xAxisRotation) * cyp + (ay + y) / 2;\n      const startAngle = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);\n      const delta = svgAngle(\n        (x1p - cxp) / rx,\n        (y1p - cyp) / ry,\n        (-x1p - cxp) / rx,\n        (-y1p - cyp) / ry\n      ) % TWO_PI;\n      const endAngle = startAngle + delta;\n      const clockwise = sweepFlag === 0;\n      renderArcEstimate(\n        ctx,\n        cx,\n        cy,\n        rx,\n        ry,\n        startAngle,\n        endAngle,\n        clockwise,\n        xAxisRotation\n      );\n    }\n  };\n  var Renderer = class extends Events {\n    constructor(params) {\n      super();\n      const smoothing = params.smoothing !== false;\n      this.domElement = params.domElement || document.createElement(\"canvas\");\n      this.ctx = this.domElement.getContext(\"2d\");\n      this.overdraw = params.overdraw || false;\n      if (typeof this.ctx.imageSmoothingEnabled !== \"undefined\") {\n        this.ctx.imageSmoothingEnabled = smoothing;\n      }\n      this.scene = new Group();\n      this.scene.parent = this;\n    }\n    setSize(width, height, ratio) {\n      this.width = width;\n      this.height = height;\n      this.ratio = typeof ratio === \"undefined\" ? getRatio(this.ctx) : ratio;\n      this.domElement.width = width * this.ratio;\n      this.domElement.height = height * this.ratio;\n      if (this.domElement.style) {\n        _.extend(this.domElement.style, {\n          width: width + \"px\",\n          height: height + \"px\"\n        });\n      }\n      return this.trigger(Events.Types.resize, width, height, ratio);\n    }\n    render() {\n      const isOne = this.ratio === 1;\n      if (!isOne) {\n        this.ctx.save();\n        this.ctx.scale(this.ratio, this.ratio);\n      }\n      if (!this.overdraw) {\n        this.ctx.clearRect(0, 0, this.width, this.height);\n      }\n      canvas.group.render.call(this.scene, this.ctx);\n      if (!isOne) {\n        this.ctx.restore();\n      }\n      return this;\n    }\n  };\n  __publicField(Renderer, \"Utils\", canvas);\n  function renderArcEstimate(ctx, ox, oy, rx, ry, startAngle, endAngle, clockwise, xAxisRotation) {\n    const delta = endAngle - startAngle;\n    const epsilon = Curve.Tolerance.epsilon;\n    const samePoints = Math.abs(delta) < epsilon;\n    let deltaAngle = mod(delta, TWO_PI);\n    if (deltaAngle < epsilon) {\n      if (samePoints) {\n        deltaAngle = 0;\n      } else {\n        deltaAngle = TWO_PI;\n      }\n    }\n    if (clockwise === true && !samePoints) {\n      if (deltaAngle === TWO_PI) {\n        deltaAngle = -TWO_PI;\n      } else {\n        deltaAngle = deltaAngle - TWO_PI;\n      }\n    }\n    for (let i = 0; i < Constants.Resolution; i++) {\n      const t = i / (Constants.Resolution - 1);\n      const angle = startAngle + t * deltaAngle;\n      let x = ox + rx * Math.cos(angle);\n      let y = oy + ry * Math.sin(angle);\n      if (xAxisRotation !== 0) {\n        const cos7 = Math.cos(xAxisRotation);\n        const sin7 = Math.sin(xAxisRotation);\n        const tx = x - ox;\n        const ty = y - oy;\n        x = tx * cos7 - ty * sin7 + ox;\n        y = tx * sin7 + ty * cos7 + oy;\n      }\n      ctx.lineTo(x, y);\n    }\n  }\n  function svgAngle(ux, uy, vx, vy) {\n    const dot = ux * vx + uy * vy;\n    const len = sqrt(ux * ux + uy * uy) * sqrt(vx * vx + vy * vy);\n    let ang = acos(max2(-1, min2(1, dot / len)));\n    if (ux * vy - uy * vx < 0) {\n      ang = -ang;\n    }\n    return ang;\n  }\n  function isDefaultMatrix(m) {\n    return m[0] == 1 && m[3] == 0 && m[1] == 0 && m[4] == 1 && m[2] == 0 && m[5] == 0;\n  }\n\n  // src/utils/canvas-shim.js\n  var CanvasShim = {\n    Image: null,\n    isHeadless: false,\n    shim: function(canvas3, Image) {\n      Renderer.Utils.shim(canvas3);\n      if (typeof Image !== \"undefined\") {\n        CanvasShim.Image = Image;\n      }\n      CanvasShim.isHeadless = true;\n      return canvas3;\n    }\n  };\n\n  // src/utils/dom.js\n  var dom = {\n    hasEventListeners: typeof root.addEventListener === \"function\",\n    bind: function(elem, event, func, bool) {\n      if (this.hasEventListeners) {\n        elem.addEventListener(event, func, !!bool);\n      } else {\n        elem.attachEvent(\"on\" + event, func);\n      }\n      return dom;\n    },\n    unbind: function(elem, event, func, bool) {\n      if (dom.hasEventListeners) {\n        elem.removeEventListeners(event, func, !!bool);\n      } else {\n        elem.detachEvent(\"on\" + event, func);\n      }\n      return dom;\n    },\n    getRequestAnimationFrame: function() {\n      const vendors = [\"ms\", \"moz\", \"webkit\", \"o\"];\n      let lastTime = 0;\n      let request = root.requestAnimationFrame;\n      if (!request) {\n        for (let i = 0; i < vendors.length; i++) {\n          request = root[vendors[i] + \"RequestAnimationFrame\"] || request;\n        }\n        request = request || fallbackRequest;\n      }\n      function fallbackRequest(callback, element) {\n        const currTime = new Date().getTime();\n        const timeToCall = Math.max(0, 16 - (currTime - lastTime));\n        const id = root.setTimeout(nextRequest, timeToCall);\n        lastTime = currTime + timeToCall;\n        function nextRequest() {\n          callback(currTime + timeToCall);\n        }\n        return id;\n      }\n      return request;\n    }\n  };\n  var temp = root.document ? root.document.createElement(\"div\") : {};\n  temp.id = \"help-two-load\";\n  Object.defineProperty(dom, \"temp\", {\n    enumerable: true,\n    get: function() {\n      if (_.isElement(temp) && !root.document.head.contains(temp)) {\n        temp.style.display = \"none\";\n        root.document.head.appendChild(temp);\n      }\n      return temp;\n    }\n  });\n\n  // src/utils/error.js\n  var TwoError = class extends Error {\n    name = \"Two.js\";\n    message;\n    constructor(message) {\n      super();\n      this.message = message;\n    }\n  };\n\n  // src/registry.js\n  var Registry = class {\n    map = {};\n    constructor() {\n    }\n    add(id, obj) {\n      this.map[id] = obj;\n      return this;\n    }\n    remove(id) {\n      delete this.map[id];\n      return this;\n    }\n    get(id) {\n      return this.map[id];\n    }\n    contains(id) {\n      return id in this.map;\n    }\n  };\n\n  // src/utils/shape.js\n  function contains(path, t) {\n    if (t === 0 || t === 1) {\n      return true;\n    }\n    const length = path._length;\n    const target = length * t;\n    let elapsed = 0;\n    for (let i = 0; i < path._lengths.length; i++) {\n      const dist = path._lengths[i];\n      if (elapsed >= target) {\n        return target - elapsed >= 0;\n      }\n      elapsed += dist;\n    }\n    return false;\n  }\n  function getIdByLength(path, target) {\n    const total = path._length;\n    if (target <= 0) {\n      return 0;\n    } else if (target >= total) {\n      return path._lengths.length - 1;\n    }\n    for (let i = 0, sum = 0; i < path._lengths.length; i++) {\n      if (sum + path._lengths[i] >= target) {\n        target -= sum;\n        return Math.max(i - 1, 0) + target / path._lengths[i];\n      }\n      sum += path._lengths[i];\n    }\n    return -1;\n  }\n  function getCurveLength2(a, b, limit) {\n    let x1, x2, x3, x4, y1, y2, y3, y4;\n    const right = b.controls && b.controls.right;\n    const left = a.controls && a.controls.left;\n    x1 = b.x;\n    y1 = b.y;\n    x2 = (right || b).x;\n    y2 = (right || b).y;\n    x3 = (left || a).x;\n    y3 = (left || a).y;\n    x4 = a.x;\n    y4 = a.y;\n    if (right && b._relative) {\n      x2 += b.x;\n      y2 += b.y;\n    }\n    if (left && a._relative) {\n      x3 += a.x;\n      y3 += a.y;\n    }\n    return getCurveLength(x1, y1, x2, y2, x3, y3, x4, y4, limit);\n  }\n  function getSubdivisions(a, b, limit) {\n    let x1, x2, x3, x4, y1, y2, y3, y4;\n    const right = b.controls && b.controls.right;\n    const left = a.controls && a.controls.left;\n    x1 = b.x;\n    y1 = b.y;\n    x2 = (right || b).x;\n    y2 = (right || b).y;\n    x3 = (left || a).x;\n    y3 = (left || a).y;\n    x4 = a.x;\n    y4 = a.y;\n    if (right && b._relative) {\n      x2 += b.x;\n      y2 += b.y;\n    }\n    if (left && a._relative) {\n      x3 += a.x;\n      y3 += a.y;\n    }\n    return subdivide(x1, y1, x2, y2, x3, y3, x4, y4, limit);\n  }\n\n  // src/effects/stop.js\n  var _Stop = class extends Element {\n    _flagOffset = true;\n    _flagOpacity = true;\n    _flagColor = true;\n    _offset = 0;\n    _opacity = 1;\n    _color = \"#fff\";\n    constructor(offset, color, opacity) {\n      super();\n      for (let prop in proto6) {\n        Object.defineProperty(this, prop, proto6[prop]);\n      }\n      this._renderer.type = \"stop\";\n      this.offset = typeof offset === \"number\" ? offset : _Stop.Index <= 0 ? 0 : 1;\n      this.opacity = typeof opacity === \"number\" ? opacity : 1;\n      this.color = typeof color === \"string\" ? color : _Stop.Index <= 0 ? \"#fff\" : \"#000\";\n      _Stop.Index = (_Stop.Index + 1) % 2;\n    }\n    clone(parent) {\n      const clone = new _Stop();\n      _.each(_Stop.Properties, function(property) {\n        clone[property] = this[property];\n      }, this);\n      if (parent && parent.stops) {\n        parent.stops.push(clone);\n      }\n      return clone;\n    }\n    toObject() {\n      const result = {};\n      _.each(_Stop.Properties, function(k) {\n        result[k] = this[k];\n      }, this);\n      return result;\n    }\n    flagReset() {\n      this._flagOffset = this._flagColor = this._flagOpacity = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var Stop = _Stop;\n  __publicField(Stop, \"Index\", 0);\n  __publicField(Stop, \"Properties\", [\"offset\", \"opacity\", \"color\"]);\n  var proto6 = {\n    offset: {\n      enumerable: true,\n      get: function() {\n        return this._offset;\n      },\n      set: function(v) {\n        this._offset = v;\n        this._flagOffset = true;\n        if (this.parent) {\n          this.parent._flagStops = true;\n        }\n      }\n    },\n    opacity: {\n      enumerable: true,\n      get: function() {\n        return this._opacity;\n      },\n      set: function(v) {\n        this._opacity = v;\n        this._flagOpacity = true;\n        if (this.parent) {\n          this.parent._flagStops = true;\n        }\n      }\n    },\n    color: {\n      enumerable: true,\n      get: function() {\n        return this._color;\n      },\n      set: function(v) {\n        this._color = v;\n        this._flagColor = true;\n        if (this.parent) {\n          this.parent._flagStops = true;\n        }\n      }\n    }\n  };\n\n  // src/effects/gradient.js\n  var _Gradient = class extends Element {\n    _flagStops = false;\n    _flagSpread = false;\n    _flagUnits = false;\n    _spread = \"\";\n    _units = \"\";\n    constructor(stops) {\n      super();\n      for (let prop in proto7) {\n        Object.defineProperty(this, prop, proto7[prop]);\n      }\n      this._renderer.type = \"gradient\";\n      this.id = Constants.Identifier + Constants.uniqueId();\n      this.classList = [];\n      this._renderer.flagStops = FlagStops.bind(this);\n      this._renderer.bindStops = BindStops.bind(this);\n      this._renderer.unbindStops = UnbindStops.bind(this);\n      this.spread = \"pad\";\n      this.units = \"objectBoundingBox\";\n      if (stops) {\n        this.stops = stops;\n      }\n    }\n    clone(parent) {\n      const stops = this.stops.map(function(s) {\n        return s.clone();\n      });\n      const clone = new _Gradient(stops);\n      _.each(_Gradient.Properties, function(k) {\n        clone[k] = this[k];\n      }, this);\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    toObject() {\n      const result = {\n        stops: this.stops.map(function(s) {\n          return s.toObject();\n        })\n      };\n      _.each(_Gradient.Properties, function(k) {\n        result[k] = this[k];\n      }, this);\n      return result;\n    }\n    _update() {\n      if (this._flagSpread || this._flagStops) {\n        this.trigger(Events.Types.change);\n      }\n      return this;\n    }\n    flagReset() {\n      this._flagSpread = this._flagUnits = this._flagStops = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var Gradient = _Gradient;\n  __publicField(Gradient, \"Stop\", Stop);\n  __publicField(Gradient, \"Properties\", [\"spread\", \"stops\", \"renderer\", \"units\"]);\n  var proto7 = {\n    spread: {\n      enumerable: true,\n      get: function() {\n        return this._spread;\n      },\n      set: function(v) {\n        this._spread = v;\n        this._flagSpread = true;\n      }\n    },\n    units: {\n      enumerable: true,\n      get: function() {\n        return this._units;\n      },\n      set: function(v) {\n        this._units = v;\n        this._flagUnits = true;\n      }\n    },\n    stops: {\n      enumerable: true,\n      get: function() {\n        return this._stops;\n      },\n      set: function(stops) {\n        const bindStops = this._renderer.bindStops;\n        const unbindStops = this._renderer.unbindStops;\n        if (this._stops) {\n          this._stops.unbind(Events.Types.insert, bindStops).unbind(Events.Types.remove, unbindStops);\n        }\n        this._stops = new Collection((stops || []).slice(0));\n        this._stops.bind(Events.Types.insert, bindStops).bind(Events.Types.remove, unbindStops);\n        bindStops(this._stops);\n      }\n    }\n  };\n  function FlagStops() {\n    this._flagStops = true;\n  }\n  function BindStops(items) {\n    let i = items.length;\n    while (i--) {\n      items[i].bind(Events.Types.change, this._renderer.flagStops);\n      items[i].parent = this;\n    }\n    this._renderer.flagStops();\n  }\n  function UnbindStops(items) {\n    let i = items.length;\n    while (i--) {\n      items[i].unbind(Events.Types.change, this._renderer.flagStops);\n      delete items[i].parent;\n    }\n    this._renderer.flagStops();\n  }\n\n  // src/effects/linear-gradient.js\n  var _LinearGradient = class extends Gradient {\n    _flagEndPoints = false;\n    _left = null;\n    _right = null;\n    constructor(x1, y1, x2, y2, stops) {\n      super(stops);\n      for (let prop in proto8) {\n        Object.defineProperty(this, prop, proto8[prop]);\n      }\n      this._renderer.type = \"linear-gradient\";\n      this._renderer.flagEndPoints = FlagEndPoints.bind(this);\n      this.left = new Vector();\n      this.right = new Vector();\n      if (typeof x1 === \"number\") {\n        this.left.x = x1;\n      }\n      if (typeof y1 === \"number\") {\n        this.left.y = y1;\n      }\n      if (typeof x2 === \"number\") {\n        this.right.x = x2;\n      }\n      if (typeof y2 === \"number\") {\n        this.right.y = y2;\n      }\n    }\n    clone(parent) {\n      const stops = this.stops.map(function(stop) {\n        return stop.clone();\n      });\n      const clone = new _LinearGradient(\n        this.left._x,\n        this.left._y,\n        this.right._x,\n        this.right._y,\n        stops\n      );\n      _.each(Gradient.Properties, function(k) {\n        clone[k] = this[k];\n      }, this);\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    toObject() {\n      const result = super.toObject.call(this);\n      result.left = this.left.toObject();\n      result.right = this.right.toObject();\n      return result;\n    }\n    _update() {\n      if (this._flagEndPoints || this._flagSpread || this._flagStops) {\n        this.trigger(Events.Types.change);\n      }\n      return this;\n    }\n    flagReset() {\n      this._flagEndPoints = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var LinearGradient = _LinearGradient;\n  __publicField(LinearGradient, \"Properties\", [\"left\", \"right\"]);\n  __publicField(LinearGradient, \"Stop\", Stop);\n  var proto8 = {\n    left: {\n      enumerable: true,\n      get: function() {\n        return this._left;\n      },\n      set: function(v) {\n        if (this._left instanceof Vector) {\n          this._left.unbind(Events.Types.change, this._renderer.flagEndPoints);\n        }\n        this._left = v;\n        this._left.bind(Events.Types.change, this._renderer.flagEndPoints);\n        this._flagEndPoints = true;\n      }\n    },\n    right: {\n      enumerable: true,\n      get: function() {\n        return this._right;\n      },\n      set: function(v) {\n        if (this._right instanceof Vector) {\n          this._right.unbind(Events.Types.change, this._renderer.flagEndPoints);\n        }\n        this._right = v;\n        this._right.bind(Events.Types.change, this._renderer.flagEndPoints);\n        this._flagEndPoints = true;\n      }\n    }\n  };\n  function FlagEndPoints() {\n    this._flagEndPoints = true;\n  }\n\n  // src/effects/radial-gradient.js\n  var _RadialGradient = class extends Gradient {\n    _flagRadius = false;\n    _flagCenter = false;\n    _flagFocal = false;\n    _radius = 0;\n    _center = null;\n    _focal = null;\n    constructor(cx, cy, r, stops, fx, fy) {\n      super(stops);\n      for (let prop in proto9) {\n        Object.defineProperty(this, prop, proto9[prop]);\n      }\n      this._renderer.type = \"radial-gradient\";\n      this._renderer.flagCenter = FlagCenter.bind(this);\n      this._renderer.flagFocal = FlagFocal.bind(this);\n      this.center = new Vector();\n      this.radius = typeof r === \"number\" ? r : 1;\n      this.focal = new Vector();\n      if (typeof cx === \"number\") {\n        this.center.x = cx;\n      }\n      if (typeof cy === \"number\") {\n        this.center.y = cy;\n      }\n      this.focal.copy(this.center);\n      if (typeof fx === \"number\") {\n        this.focal.x = fx;\n      }\n      if (typeof fy === \"number\") {\n        this.focal.y = fy;\n      }\n    }\n    clone(parent) {\n      const stops = this.stops.map(function(stop) {\n        return stop.clone();\n      });\n      const clone = new _RadialGradient(\n        this.center._x,\n        this.center._y,\n        this._radius,\n        stops,\n        this.focal._x,\n        this.focal._y\n      );\n      _.each(Gradient.Properties.concat(_RadialGradient.Properties), function(k) {\n        clone[k] = this[k];\n      }, this);\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    toObject() {\n      const result = super.toObject.call(this);\n      _.each(_RadialGradient.Properties, function(k) {\n        result[k] = this[k];\n      }, this);\n      result.center = this.center.toObject();\n      result.focal = this.focal.toObject();\n      return result;\n    }\n    _update() {\n      if (this._flagRadius || this._flatCenter || this._flagFocal || this._flagSpread || this._flagStops) {\n        this.trigger(Events.Types.change);\n      }\n      return this;\n    }\n    flagReset() {\n      this._flagRadius = this._flagCenter = this._flagFocal = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var RadialGradient = _RadialGradient;\n  __publicField(RadialGradient, \"Stop\", Stop);\n  __publicField(RadialGradient, \"Properties\", [\"center\", \"radius\", \"focal\"]);\n  var proto9 = {\n    radius: {\n      enumerable: true,\n      get: function() {\n        return this._radius;\n      },\n      set: function(v) {\n        this._radius = v;\n        this._flagRadius = true;\n      }\n    },\n    center: {\n      enumerable: true,\n      get: function() {\n        return this._center;\n      },\n      set: function(v) {\n        if (this._center) {\n          this._center.unbind(Events.Types.change, this._renderer.flagCenter);\n        }\n        this._center = v;\n        this._center.bind(Events.Types.change, this._renderer.flagCenter);\n        this._flagCenter = true;\n      }\n    },\n    focal: {\n      enumerable: true,\n      get: function() {\n        return this._focal;\n      },\n      set: function(v) {\n        if (this._focal) {\n          this._focal.unbind(Events.Types.change, this._renderer.flagFocal);\n        }\n        this._focal = v;\n        this._focal.bind(Events.Types.change, this._renderer.flagFocal);\n        this._flagFocal = true;\n      }\n    }\n  };\n  function FlagCenter() {\n    this._flagCenter = true;\n  }\n  function FlagFocal() {\n    this._flagFocal = true;\n  }\n\n  // src/effects/texture.js\n  var anchor;\n  var regex = {\n    video: /\\.(mp4|webm|ogg)$/i,\n    image: /\\.(jpe?g|png|gif|tiff|webp)$/i,\n    effect: /texture|gradient/i\n  };\n  if (root.document) {\n    anchor = document.createElement(\"a\");\n  }\n  var _Texture = class extends Element {\n    _flagSrc = false;\n    _flagImage = false;\n    _flagVideo = false;\n    _flagLoaded = false;\n    _flagRepeat = false;\n    _flagOffset = false;\n    _flagScale = false;\n    _src = \"\";\n    _image = null;\n    _loaded = false;\n    _repeat = \"no-repeat\";\n    _scale = 1;\n    _offset = null;\n    constructor(src, callback) {\n      super();\n      this._renderer = {};\n      for (let prop in proto10) {\n        Object.defineProperty(this, prop, proto10[prop]);\n      }\n      this._renderer.type = \"texture\";\n      this._renderer.flagOffset = FlagOffset.bind(this);\n      this._renderer.flagScale = FlagScale.bind(this);\n      this.id = Constants.Identifier + Constants.uniqueId();\n      this.classList = [];\n      this.loaded = false;\n      this.repeat = \"no-repeat\";\n      this.offset = new Vector();\n      if (typeof callback === \"function\") {\n        const loaded = function() {\n          this.unbind(Events.Types.load, loaded);\n          if (typeof callback === \"function\") {\n            callback();\n          }\n        }.bind(this);\n        this.bind(Events.Types.load, loaded);\n      }\n      if (typeof src === \"string\") {\n        this.src = src;\n      } else if (typeof src === \"object\") {\n        const elemString = Object.prototype.toString.call(src);\n        if (elemString === \"[object HTMLImageElement]\" || elemString === \"[object HTMLCanvasElement]\" || elemString === \"[object HTMLVideoElement]\" || elemString === \"[object Image]\") {\n          this.image = src;\n        }\n      }\n      this._update();\n    }\n    static getAbsoluteURL(path) {\n      if (!anchor) {\n        return path;\n      }\n      anchor.href = path;\n      return anchor.href;\n    }\n    static loadHeadlessBuffer(texture, loaded) {\n      texture.image.onload = loaded;\n      texture.image.src = texture.src;\n    }\n    static getTag(image) {\n      return image && image.nodeName && image.nodeName.toLowerCase() || \"img\";\n    }\n    static getImage(src) {\n      const absoluteSrc = _Texture.getAbsoluteURL(src);\n      if (_Texture.ImageRegistry.contains(absoluteSrc)) {\n        return _Texture.ImageRegistry.get(absoluteSrc);\n      }\n      let image;\n      if (CanvasShim.Image) {\n        image = new CanvasShim.Image();\n        Renderer.Utils.shim(image, \"img\");\n      } else if (root.document) {\n        if (regex.video.test(absoluteSrc)) {\n          image = document.createElement(\"video\");\n        } else {\n          image = document.createElement(\"img\");\n        }\n      } else {\n        console.warn(\"Two.js: no prototypical image defined for Two.Texture\");\n      }\n      image.crossOrigin = \"anonymous\";\n      image.referrerPolicy = \"no-referrer\";\n      return image;\n    }\n    static load(texture, callback) {\n      let image = texture.image;\n      let tag = _Texture.getTag(image);\n      if (texture._flagImage) {\n        if (/canvas/i.test(tag)) {\n          _Texture.Register.canvas(texture, callback);\n        } else {\n          texture._src = !CanvasShim.isHeadless && image.getAttribute(\"two-src\") || image.src;\n          _Texture.Register[tag](texture, callback);\n        }\n      }\n      if (texture._flagSrc) {\n        if (!image) {\n          image = _Texture.getImage(texture.src);\n          texture.image = image;\n        }\n        tag = _Texture.getTag(image);\n        _Texture.Register[tag](texture, callback);\n      }\n    }\n    clone() {\n      const clone = new _Texture(this.src);\n      clone.repeat = this.repeat;\n      clone.offset.copy(this.origin);\n      clone.scale = this.scale;\n      return clone;\n    }\n    toObject() {\n      return {\n        src: this.src,\n        repeat: this.repeat,\n        origin: this.origin.toObject(),\n        scale: typeof this.scale === \"number\" ? this.scale : this.scale.toObject()\n      };\n    }\n    _update() {\n      if (this._flagSrc || this._flagImage) {\n        this.trigger(Events.Types.change);\n        if (this._flagSrc || this._flagImage) {\n          this.loaded = false;\n          _Texture.load(this, function() {\n            this.loaded = true;\n            this.trigger(Events.Types.change).trigger(Events.Types.load);\n          }.bind(this));\n        }\n      }\n      if (this._image && this._image.readyState >= 4) {\n        this._flagVideo = true;\n      }\n      return this;\n    }\n    flagReset() {\n      this._flagSrc = this._flagImage = this._flagLoaded = this._flagRepeat = this._flagVideo = this._flagScale = this._flagOffset = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var Texture = _Texture;\n  __publicField(Texture, \"Properties\", [\n    \"src\",\n    \"loaded\",\n    \"repeat\",\n    \"scale\",\n    \"offset\",\n    \"image\"\n  ]);\n  __publicField(Texture, \"RegularExpressions\", regex);\n  __publicField(Texture, \"ImageRegistry\", new Registry());\n  __publicField(Texture, \"Register\", {\n    canvas: function(texture, callback) {\n      texture._src = \"#\" + texture.id;\n      _Texture.ImageRegistry.add(texture.src, texture.image);\n      if (typeof callback === \"function\") {\n        callback();\n      }\n    },\n    img: function(texture, callback) {\n      const image = texture.image;\n      const loaded = function(e) {\n        if (!CanvasShim.isHeadless && image.removeEventListener && typeof image.removeEventListener === \"function\") {\n          image.removeEventListener(\"load\", loaded, false);\n          image.removeEventListener(\"error\", error, false);\n        }\n        if (typeof callback === \"function\") {\n          callback();\n        }\n      };\n      const error = function(e) {\n        if (!CanvasShim.isHeadless && typeof image.removeEventListener === \"function\") {\n          image.removeEventListener(\"load\", loaded, false);\n          image.removeEventListener(\"error\", error, false);\n        }\n        throw new TwoError(\"unable to load \" + texture.src);\n      };\n      if (typeof image.width === \"number\" && image.width > 0 && typeof image.height === \"number\" && image.height > 0) {\n        loaded();\n      } else if (!CanvasShim.isHeadless && typeof image.addEventListener === \"function\") {\n        image.addEventListener(\"load\", loaded, false);\n        image.addEventListener(\"error\", error, false);\n      }\n      texture._src = _Texture.getAbsoluteURL(texture._src);\n      if (!CanvasShim.isHeadless && image && image.getAttribute(\"two-src\")) {\n        return;\n      }\n      if (!CanvasShim.isHeadless) {\n        image.setAttribute(\"two-src\", texture.src);\n      }\n      _Texture.ImageRegistry.add(texture.src, image);\n      if (CanvasShim.isHeadless) {\n        _Texture.loadHeadlessBuffer(texture, loaded);\n      } else {\n        texture.image.src = texture.src;\n      }\n    },\n    video: function(texture, callback) {\n      if (CanvasShim.isHeadless) {\n        throw new TwoError(\"video textures are not implemented in headless environments.\");\n      }\n      const loaded = function(e) {\n        texture.image.removeEventListener(\"canplaythrough\", loaded, false);\n        texture.image.removeEventListener(\"error\", error, false);\n        texture.image.width = texture.image.videoWidth;\n        texture.image.height = texture.image.videoHeight;\n        if (typeof callback === \"function\") {\n          callback();\n        }\n      };\n      const error = function(e) {\n        texture.image.removeEventListener(\"canplaythrough\", loaded, false);\n        texture.image.removeEventListener(\"error\", error, false);\n        throw new TwoError(\"unable to load \" + texture.src);\n      };\n      texture._src = _Texture.getAbsoluteURL(texture._src);\n      if (!texture.image.getAttribute(\"two-src\")) {\n        texture.image.setAttribute(\"two-src\", texture.src);\n        _Texture.ImageRegistry.add(texture.src, texture.image);\n      }\n      if (texture.image.readyState >= 4) {\n        loaded();\n      } else {\n        texture.image.addEventListener(\"canplaythrough\", loaded, false);\n        texture.image.addEventListener(\"error\", error, false);\n        texture.image.src = texture.src;\n        texture.image.load();\n      }\n    }\n  });\n  var proto10 = {\n    src: {\n      enumerable: true,\n      get: function() {\n        return this._src;\n      },\n      set: function(v) {\n        this._src = v;\n        this._flagSrc = true;\n      }\n    },\n    loaded: {\n      enumerable: true,\n      get: function() {\n        return this._loaded;\n      },\n      set: function(v) {\n        this._loaded = v;\n        this._flagLoaded = true;\n      }\n    },\n    repeat: {\n      enumerable: true,\n      get: function() {\n        return this._repeat;\n      },\n      set: function(v) {\n        this._repeat = v;\n        this._flagRepeat = true;\n      }\n    },\n    image: {\n      enumerable: true,\n      get: function() {\n        return this._image;\n      },\n      set: function(image) {\n        const tag = Texture.getTag(image);\n        let index;\n        switch (tag) {\n          case \"canvas\":\n            index = \"#\" + image.id;\n            break;\n          default:\n            index = image.src;\n        }\n        if (Texture.ImageRegistry.contains(index)) {\n          this._image = Texture.ImageRegistry.get(image.src);\n        } else {\n          this._image = image;\n        }\n        this._flagImage = true;\n      }\n    },\n    offset: {\n      enumerable: true,\n      get: function() {\n        return this._offset;\n      },\n      set: function(v) {\n        if (this._offset) {\n          this._offset.unbind(Events.Types.change, this._renderer.flagOffset);\n        }\n        this._offset = v;\n        this._offset.bind(Events.Types.change, this._renderer.flagOffset);\n        this._flagOffset = true;\n      }\n    },\n    scale: {\n      enumerable: true,\n      get: function() {\n        return this._scale;\n      },\n      set: function(v) {\n        if (this._scale instanceof Vector) {\n          this._scale.unbind(Events.Types.change, this._renderer.flagScale);\n        }\n        this._scale = v;\n        if (this._scale instanceof Vector) {\n          this._scale.bind(Events.Types.change, this._renderer.flagScale);\n        }\n        this._flagScale = true;\n      }\n    }\n  };\n  function FlagOffset() {\n    this._flagOffset = true;\n  }\n  function FlagScale() {\n    this._flagScale = true;\n  }\n\n  // src/path.js\n  var min3 = Math.min;\n  var max3 = Math.max;\n  var ceil = Math.ceil;\n  var floor2 = Math.floor;\n  var vector = new Vector();\n  var _Path = class extends Shape {\n    _flagVertices = true;\n    _flagLength = true;\n    _flagFill = true;\n    _flagStroke = true;\n    _flagLinewidth = true;\n    _flagOpacity = true;\n    _flagVisible = true;\n    _flagCap = true;\n    _flagJoin = true;\n    _flagMiter = true;\n    _flagMask = false;\n    _flagClip = false;\n    _length = 0;\n    _fill = \"#fff\";\n    _stroke = \"#000\";\n    _linewidth = 1;\n    _opacity = 1;\n    _visible = true;\n    _cap = \"round\";\n    _join = \"round\";\n    _miter = 4;\n    _closed = true;\n    _curved = false;\n    _automatic = true;\n    _beginning = 0;\n    _ending = 1;\n    _mask = null;\n    _clip = false;\n    _dashes = null;\n    constructor(vertices, closed2, curved, manual) {\n      super();\n      for (let prop in proto11) {\n        Object.defineProperty(this, prop, proto11[prop]);\n      }\n      this._renderer.type = \"path\";\n      this._renderer.flagVertices = FlagVertices.bind(this);\n      this._renderer.bindVertices = BindVertices.bind(this);\n      this._renderer.unbindVertices = UnbindVertices.bind(this);\n      this._renderer.flagFill = FlagFill.bind(this);\n      this._renderer.flagStroke = FlagStroke.bind(this);\n      this._renderer.vertices = [];\n      this._renderer.collection = [];\n      this.closed = !!closed2;\n      this.curved = !!curved;\n      this.beginning = 0;\n      this.ending = 1;\n      this.fill = \"#fff\";\n      this.stroke = \"#000\";\n      this.linewidth = 1;\n      this.opacity = 1;\n      this.className = \"\";\n      this.visible = true;\n      this.cap = \"butt\";\n      this.join = \"miter\";\n      this.miter = 4;\n      this.vertices = vertices;\n      this.automatic = !manual;\n      this.dashes = [];\n      this.dashes.offset = 0;\n    }\n    clone(parent) {\n      const clone = new _Path();\n      for (let j = 0; j < this.vertices.length; j++) {\n        clone.vertices.push(this.vertices[j].clone());\n      }\n      for (let i = 0; i < _Path.Properties.length; i++) {\n        const k = _Path.Properties[i];\n        clone[k] = this[k];\n      }\n      clone.className = this.className;\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone._update();\n    }\n    toObject() {\n      const result = {\n        vertices: this.vertices.map(function(v) {\n          return v.toObject();\n        })\n      };\n      _.each(_Path.Properties, function(k) {\n        if (typeof this[k] !== \"undefined\") {\n          if (this[k].toObject) {\n            result[k] = this[k].toObject();\n          } else {\n            result[k] = this[k];\n          }\n        }\n      }, this);\n      result.className = this.className;\n      result.translation = this.translation.toObject();\n      result.rotation = this.rotation;\n      result.scale = this.scale instanceof Vector ? this.scale.toObject() : this.scale;\n      result.skewX = this.skewX;\n      result.skewY = this.skewY;\n      if (this.matrix.manual) {\n        result.matrix = this.matrix.toObject();\n      }\n      return result;\n    }\n    noFill() {\n      this.fill = \"none\";\n      return this;\n    }\n    noStroke() {\n      this.stroke = \"none\";\n      return this;\n    }\n    corner() {\n      const rect = this.getBoundingClientRect(true);\n      const hw = rect.width / 2;\n      const hh = rect.height / 2;\n      const cx = rect.left + rect.width / 2;\n      const cy = rect.top + rect.height / 2;\n      for (let i = 0; i < this.vertices.length; i++) {\n        const v = this.vertices[i];\n        v.x -= cx;\n        v.y -= cy;\n        v.x += hw;\n        v.y += hh;\n      }\n      if (this.mask) {\n        this.mask.translation.x -= cx;\n        this.mask.translation.x += hw;\n        this.mask.translation.y -= cy;\n        this.mask.translation.y += hh;\n      }\n      return this;\n    }\n    center() {\n      const rect = this.getBoundingClientRect(true);\n      const cx = rect.left + rect.width / 2 - this.translation.x;\n      const cy = rect.top + rect.height / 2 - this.translation.y;\n      for (let i = 0; i < this.vertices.length; i++) {\n        const v = this.vertices[i];\n        v.x -= cx;\n        v.y -= cy;\n      }\n      if (this.mask) {\n        this.mask.translation.x -= cx;\n        this.mask.translation.y -= cy;\n      }\n      return this;\n    }\n    getBoundingClientRect(shallow) {\n      let matrix, border, l, i, v0, v1;\n      let left = Infinity, right = -Infinity, top = Infinity, bottom = -Infinity;\n      this._update(true);\n      matrix = shallow ? this.matrix : this.worldMatrix;\n      border = (this.linewidth || 0) / 2;\n      l = this._renderer.vertices.length;\n      if (this.linewidth > 0 || this.stroke && !/(transparent|none)/i.test(this.stroke)) {\n        if (this.matrix.manual) {\n          const { scaleX, scaleY } = decomposeMatrix(\n            matrix.elements[0],\n            matrix.elements[3],\n            matrix.elements[1],\n            matrix.elements[4],\n            matrix.elements[2],\n            matrix.elements[5]\n          );\n          if (typeof scaleX === \"number\" && typeof scaleY === \"number\") {\n            border = Math.max(scaleX, scaleY) * (this.linewidth || 0) / 2;\n          }\n        } else {\n          border *= typeof this.scale === \"number\" ? this.scale : Math.max(this.scale.x, this.scale.y);\n        }\n      }\n      if (l <= 0) {\n        return {\n          width: 0,\n          height: 0\n        };\n      }\n      for (i = 0; i < l; i++) {\n        v1 = this._renderer.vertices[i];\n        v0 = this._renderer.vertices[(i + l - 1) % l];\n        const [v0x, v0y] = matrix.multiply(v0.x, v0.y);\n        const [v1x, v1y] = matrix.multiply(v1.x, v1.y);\n        if (v0.controls && v1.controls) {\n          let rx = v0.controls.right.x;\n          let ry = v0.controls.right.y;\n          if (v0.relative) {\n            rx += v0.x;\n            ry += v0.y;\n          }\n          let [c0x, c0y] = matrix.multiply(rx, ry);\n          let lx = v1.controls.left.x;\n          let ly = v1.controls.left.y;\n          if (v1.relative) {\n            lx += v1.x;\n            ly += v1.y;\n          }\n          let [c1x, c1y] = matrix.multiply(lx, ly);\n          const bb = getCurveBoundingBox(\n            v0x,\n            v0y,\n            c0x,\n            c0y,\n            c1x,\n            c1y,\n            v1x,\n            v1y\n          );\n          top = min3(bb.min.y - border, top);\n          left = min3(bb.min.x - border, left);\n          right = max3(bb.max.x + border, right);\n          bottom = max3(bb.max.y + border, bottom);\n        } else {\n          if (i <= 1) {\n            top = min3(v0y - border, top);\n            left = min3(v0x - border, left);\n            right = max3(v0x + border, right);\n            bottom = max3(v0y + border, bottom);\n          }\n          top = min3(v1y - border, top);\n          left = min3(v1x - border, left);\n          right = max3(v1x + border, right);\n          bottom = max3(v1y + border, bottom);\n        }\n      }\n      return {\n        top,\n        left,\n        right,\n        bottom,\n        width: right - left,\n        height: bottom - top\n      };\n    }\n    getPointAt(t, obj) {\n      let ia, ib, result;\n      let x, x1, x2, x3, x4, y, y1, y2, y3, y4, left, right;\n      let target = this.length * Math.min(Math.max(t, 0), 1);\n      const length = this.vertices.length;\n      const last = length - 1;\n      let a = null;\n      let b = null;\n      for (let i = 0, l = this._lengths.length, sum = 0; i < l; i++) {\n        if (sum + this._lengths[i] >= target) {\n          if (this._closed) {\n            ia = mod(i, length);\n            ib = mod(i - 1, length);\n            if (i === 0) {\n              ia = ib;\n              ib = i;\n            }\n          } else {\n            ia = i;\n            ib = Math.min(Math.max(i - 1, 0), last);\n          }\n          a = this.vertices[ia];\n          b = this.vertices[ib];\n          target -= sum;\n          if (this._lengths[i] !== 0) {\n            t = target / this._lengths[i];\n          } else {\n            t = 0;\n          }\n          break;\n        }\n        sum += this._lengths[i];\n      }\n      if (a === null || b === null) {\n        return null;\n      }\n      if (!a) {\n        return b;\n      } else if (!b) {\n        return a;\n      }\n      right = b.controls && b.controls.right;\n      left = a.controls && a.controls.left;\n      x1 = b.x;\n      y1 = b.y;\n      x2 = (right || b).x;\n      y2 = (right || b).y;\n      x3 = (left || a).x;\n      y3 = (left || a).y;\n      x4 = a.x;\n      y4 = a.y;\n      if (right && b.relative) {\n        x2 += b.x;\n        y2 += b.y;\n      }\n      if (left && a.relative) {\n        x3 += a.x;\n        y3 += a.y;\n      }\n      x = getComponentOnCubicBezier(t, x1, x2, x3, x4);\n      y = getComponentOnCubicBezier(t, y1, y2, y3, y4);\n      const t1x = lerp(x1, x2, t);\n      const t1y = lerp(y1, y2, t);\n      const t2x = lerp(x2, x3, t);\n      const t2y = lerp(y2, y3, t);\n      const t3x = lerp(x3, x4, t);\n      const t3y = lerp(y3, y4, t);\n      const brx = lerp(t1x, t2x, t);\n      const bry = lerp(t1y, t2y, t);\n      const alx = lerp(t2x, t3x, t);\n      const aly = lerp(t2y, t3y, t);\n      if (_.isObject(obj)) {\n        obj.x = x;\n        obj.y = y;\n        if (obj instanceof Anchor) {\n          obj.controls.left.x = brx;\n          obj.controls.left.y = bry;\n          obj.controls.right.x = alx;\n          obj.controls.right.y = aly;\n          if (!(typeof obj.relative === \"boolean\") || obj.relative) {\n            obj.controls.left.x -= x;\n            obj.controls.left.y -= y;\n            obj.controls.right.x -= x;\n            obj.controls.right.y -= y;\n          }\n        }\n        obj.t = t;\n        return obj;\n      }\n      result = new Anchor(\n        x,\n        y,\n        brx - x,\n        bry - y,\n        alx - x,\n        aly - y,\n        this._curved ? Commands.curve : Commands.line\n      );\n      result.t = t;\n      return result;\n    }\n    plot() {\n      if (this.curved) {\n        getCurveFromPoints(this._collection, this.closed);\n        return this;\n      }\n      for (let i = 0; i < this._collection.length; i++) {\n        this._collection[i].command = i === 0 ? Commands.move : Commands.line;\n      }\n      return this;\n    }\n    subdivide(limit) {\n      this._update();\n      const last = this.vertices.length - 1;\n      const closed2 = this._closed || this.vertices[last]._command === Commands.close;\n      let b = this.vertices[last];\n      let points = [], verts;\n      _.each(this.vertices, function(a, i) {\n        if (i <= 0 && !closed2) {\n          b = a;\n          return;\n        }\n        if (a.command === Commands.move) {\n          points.push(new Anchor(b.x, b.y));\n          if (i > 0) {\n            points[points.length - 1].command = Commands.line;\n          }\n          b = a;\n          return;\n        }\n        verts = getSubdivisions(a, b, limit);\n        points = points.concat(verts);\n        _.each(verts, function(v, i2) {\n          if (i2 <= 0 && b.command === Commands.move) {\n            v.command = Commands.move;\n          } else {\n            v.command = Commands.line;\n          }\n        });\n        if (i >= last) {\n          if (this._closed && this._automatic) {\n            b = a;\n            verts = getSubdivisions(a, b, limit);\n            points = points.concat(verts);\n            _.each(verts, function(v, i2) {\n              if (i2 <= 0 && b.command === Commands.move) {\n                v.command = Commands.move;\n              } else {\n                v.command = Commands.line;\n              }\n            });\n          } else if (closed2) {\n            points.push(new Anchor(a.x, a.y));\n          }\n          points[points.length - 1].command = closed2 ? Commands.close : Commands.line;\n        }\n        b = a;\n      }, this);\n      this._automatic = false;\n      this._curved = false;\n      this.vertices = points;\n      return this;\n    }\n    _updateLength(limit, silent) {\n      if (!silent) {\n        this._update();\n      }\n      const length = this.vertices.length;\n      const last = length - 1;\n      const closed2 = false;\n      let b = this.vertices[last];\n      let sum = 0;\n      if (typeof this._lengths === \"undefined\") {\n        this._lengths = [];\n      }\n      _.each(this.vertices, function(a, i) {\n        if (i <= 0 && !closed2 || a.command === Commands.move) {\n          b = a;\n          this._lengths[i] = 0;\n          return;\n        }\n        this._lengths[i] = getCurveLength2(a, b, limit);\n        sum += this._lengths[i];\n        if (i >= last && closed2) {\n          b = this.vertices[(i + 1) % length];\n          this._lengths[i + 1] = getCurveLength2(a, b, limit);\n          sum += this._lengths[i + 1];\n        }\n        b = a;\n      }, this);\n      this._length = sum;\n      this._flagLength = false;\n      return this;\n    }\n    _update() {\n      if (this._flagVertices) {\n        if (this._automatic) {\n          this.plot();\n        }\n        if (this._flagLength) {\n          this._updateLength(void 0, true);\n        }\n        const l = this._collection.length;\n        const closed2 = this._closed;\n        const beginning = Math.min(this._beginning, this._ending);\n        const ending = Math.max(this._beginning, this._ending);\n        const bid = getIdByLength(this, beginning * this._length);\n        const eid = getIdByLength(this, ending * this._length);\n        const low = ceil(bid);\n        const high = floor2(eid);\n        let left, right, prev, next, v, i;\n        this._renderer.vertices.length = 0;\n        for (i = 0; i < l; i++) {\n          if (this._renderer.collection.length <= i) {\n            this._renderer.collection.push(new Anchor());\n          }\n          if (i > high && !right) {\n            v = this._renderer.collection[i].copy(this._collection[i]);\n            this.getPointAt(ending, v);\n            v.command = this._renderer.collection[i].command;\n            this._renderer.vertices.push(v);\n            right = v;\n            prev = this._collection[i - 1];\n            if (prev && prev.controls) {\n              if (v.relative) {\n                v.controls.right.clear();\n              } else {\n                v.controls.right.copy(v);\n              }\n              if (prev.relative) {\n                this._renderer.collection[i - 1].controls.right.copy(prev.controls.right).lerp(Vector.zero, 1 - v.t);\n              } else {\n                this._renderer.collection[i - 1].controls.right.copy(prev.controls.right).lerp(prev, 1 - v.t);\n              }\n            }\n          } else if (i >= low && i <= high) {\n            v = this._renderer.collection[i].copy(this._collection[i]);\n            this._renderer.vertices.push(v);\n            if (i === high && contains(this, ending)) {\n              right = v;\n              if (!closed2 && right.controls) {\n                if (right.relative) {\n                  right.controls.right.clear();\n                } else {\n                  right.controls.right.copy(right);\n                }\n              }\n            } else if (i === low && contains(this, beginning)) {\n              left = v;\n              left.command = Commands.move;\n              if (!closed2 && left.controls) {\n                if (left.relative) {\n                  left.controls.left.clear();\n                } else {\n                  left.controls.left.copy(left);\n                }\n              }\n            }\n          }\n        }\n        if (low > 0 && !left) {\n          i = low - 1;\n          v = this._renderer.collection[i].copy(this._collection[i]);\n          this.getPointAt(beginning, v);\n          v.command = Commands.move;\n          this._renderer.vertices.unshift(v);\n          next = this._collection[i + 1];\n          if (next && next.controls) {\n            v.controls.left.clear();\n            if (next.relative) {\n              this._renderer.collection[i + 1].controls.left.copy(next.controls.left).lerp(Vector.zero, v.t);\n            } else {\n              vector.copy(next);\n              this._renderer.collection[i + 1].controls.left.copy(next.controls.left).lerp(next, v.t);\n            }\n          }\n        }\n      }\n      Shape.prototype._update.apply(this, arguments);\n      return this;\n    }\n    flagReset() {\n      this._flagVertices = this._flagLength = this._flagFill = this._flagStroke = this._flagLinewidth = this._flagOpacity = this._flagVisible = this._flagCap = this._flagJoin = this._flagMiter = this._flagClip = false;\n      Shape.prototype.flagReset.call(this);\n      return this;\n    }\n  };\n  var Path = _Path;\n  __publicField(Path, \"Properties\", [\n    \"fill\",\n    \"stroke\",\n    \"linewidth\",\n    \"opacity\",\n    \"visible\",\n    \"cap\",\n    \"join\",\n    \"miter\",\n    \"closed\",\n    \"curved\",\n    \"automatic\",\n    \"beginning\",\n    \"ending\"\n  ]);\n  __publicField(Path, \"Utils\", {\n    getCurveLength: getCurveLength2\n  });\n  var proto11 = {\n    linewidth: {\n      enumerable: true,\n      get: function() {\n        return this._linewidth;\n      },\n      set: function(v) {\n        this._linewidth = v;\n        this._flagLinewidth = true;\n      }\n    },\n    opacity: {\n      enumerable: true,\n      get: function() {\n        return this._opacity;\n      },\n      set: function(v) {\n        this._opacity = v;\n        this._flagOpacity = true;\n      }\n    },\n    visible: {\n      enumerable: true,\n      get: function() {\n        return this._visible;\n      },\n      set: function(v) {\n        this._visible = v;\n        this._flagVisible = true;\n      }\n    },\n    cap: {\n      enumerable: true,\n      get: function() {\n        return this._cap;\n      },\n      set: function(v) {\n        this._cap = v;\n        this._flagCap = true;\n      }\n    },\n    join: {\n      enumerable: true,\n      get: function() {\n        return this._join;\n      },\n      set: function(v) {\n        this._join = v;\n        this._flagJoin = true;\n      }\n    },\n    miter: {\n      enumerable: true,\n      get: function() {\n        return this._miter;\n      },\n      set: function(v) {\n        this._miter = v;\n        this._flagMiter = true;\n      }\n    },\n    fill: {\n      enumerable: true,\n      get: function() {\n        return this._fill;\n      },\n      set: function(f) {\n        if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n          this._fill.unbind(Events.Types.change, this._renderer.flagFill);\n        }\n        this._fill = f;\n        this._flagFill = true;\n        if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n          this._fill.bind(Events.Types.change, this._renderer.flagFill);\n        }\n      }\n    },\n    stroke: {\n      enumerable: true,\n      get: function() {\n        return this._stroke;\n      },\n      set: function(f) {\n        if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n          this._stroke.unbind(Events.Types.change, this._renderer.flagStroke);\n        }\n        this._stroke = f;\n        this._flagStroke = true;\n        if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n          this._stroke.bind(Events.Types.change, this._renderer.flagStroke);\n        }\n      }\n    },\n    length: {\n      get: function() {\n        if (this._flagLength) {\n          this._updateLength();\n        }\n        return this._length;\n      }\n    },\n    closed: {\n      enumerable: true,\n      get: function() {\n        return this._closed;\n      },\n      set: function(v) {\n        this._closed = !!v;\n        this._flagVertices = true;\n      }\n    },\n    curved: {\n      enumerable: true,\n      get: function() {\n        return this._curved;\n      },\n      set: function(v) {\n        this._curved = !!v;\n        this._flagVertices = true;\n      }\n    },\n    automatic: {\n      enumerable: true,\n      get: function() {\n        return this._automatic;\n      },\n      set: function(v) {\n        if (v === this._automatic) {\n          return;\n        }\n        this._automatic = !!v;\n        const method = this._automatic ? \"ignore\" : \"listen\";\n        _.each(this.vertices, function(v2) {\n          v2[method]();\n        });\n      }\n    },\n    beginning: {\n      enumerable: true,\n      get: function() {\n        return this._beginning;\n      },\n      set: function(v) {\n        this._beginning = v;\n        this._flagVertices = true;\n      }\n    },\n    ending: {\n      enumerable: true,\n      get: function() {\n        return this._ending;\n      },\n      set: function(v) {\n        this._ending = v;\n        this._flagVertices = true;\n      }\n    },\n    vertices: {\n      enumerable: true,\n      get: function() {\n        return this._collection;\n      },\n      set: function(vertices) {\n        const bindVertices = this._renderer.bindVertices;\n        const unbindVertices = this._renderer.unbindVertices;\n        if (this._collection) {\n          this._collection.unbind(Events.Types.insert, bindVertices).unbind(Events.Types.remove, unbindVertices);\n        }\n        if (vertices instanceof Collection) {\n          this._collection = vertices;\n        } else {\n          this._collection = new Collection(vertices || []);\n        }\n        this._collection.bind(Events.Types.insert, bindVertices).bind(Events.Types.remove, unbindVertices);\n        bindVertices(this._collection);\n      }\n    },\n    mask: {\n      enumerable: true,\n      get: function() {\n        return this._mask;\n      },\n      set: function(v) {\n        this._mask = v;\n        this._flagMask = true;\n        if (_.isObject(v) && !v.clip) {\n          v.clip = true;\n        }\n      }\n    },\n    clip: {\n      enumerable: true,\n      get: function() {\n        return this._clip;\n      },\n      set: function(v) {\n        this._clip = v;\n        this._flagClip = true;\n      }\n    },\n    dashes: {\n      enumerable: true,\n      get: function() {\n        return this._dashes;\n      },\n      set: function(v) {\n        if (typeof v.offset !== \"number\") {\n          v.offset = this.dashes && this._dashes.offset || 0;\n        }\n        this._dashes = v;\n      }\n    }\n  };\n  function FlagVertices() {\n    this._flagVertices = true;\n    this._flagLength = true;\n    if (this.parent) {\n      this.parent._flagLength = true;\n    }\n  }\n  function BindVertices(items) {\n    let i = items.length;\n    while (i--) {\n      items[i].bind(Events.Types.change, this._renderer.flagVertices);\n    }\n    this._renderer.flagVertices();\n  }\n  function UnbindVertices(items) {\n    let i = items.length;\n    while (i--) {\n      items[i].unbind(Events.Types.change, this._renderer.flagVertices);\n    }\n    this._renderer.flagVertices();\n  }\n  function FlagFill() {\n    this._flagFill = true;\n  }\n  function FlagStroke() {\n    this._flagStroke = true;\n  }\n\n  // src/shapes/rectangle.js\n  var _Rectangle = class extends Path {\n    constructor(x, y, width, height) {\n      const points = [\n        new Anchor(),\n        new Anchor(),\n        new Anchor(),\n        new Anchor()\n      ];\n      super(points, true, false, true);\n      for (let prop in proto12) {\n        Object.defineProperty(this, prop, proto12[prop]);\n      }\n      this.width = typeof width === \"number\" ? width : 1;\n      this.height = typeof height === \"number\" ? height : 1;\n      this.origin = new Vector();\n      if (typeof x === \"number\") {\n        this.translation.x = x;\n      }\n      if (typeof y === \"number\") {\n        this.translation.y = y;\n      }\n      this._update();\n    }\n    _flagWidth = 0;\n    _flagHeight = 0;\n    _width = 0;\n    _height = 0;\n    _origin = null;\n    _update() {\n      if (this._flagVertices || this._flagWidth || this._flagHeight) {\n        const xr = this._width / 2;\n        const yr = this._height / 2;\n        if (!this._closed && this.vertices.length === 4) {\n          this.vertices.push(new Anchor());\n        }\n        this.vertices[0].set(-xr, -yr).sub(this._origin).command = Commands.move;\n        this.vertices[1].set(xr, -yr).sub(this._origin).command = Commands.line;\n        this.vertices[2].set(xr, yr).sub(this._origin).command = Commands.line;\n        this.vertices[3].set(-xr, yr).sub(this._origin).command = Commands.line;\n        if (this.vertices[4]) {\n          this.vertices[4].set(-xr, -yr).sub(this._origin).command = Commands.line;\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    flagReset() {\n      this._flagWidth = this._flagHeight = false;\n      super.flagReset.call(this);\n      return this;\n    }\n    clone(parent) {\n      const clone = new _Rectangle(0, 0, this.width, this.height);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      for (let i = 0; i < Path.Properties.length; i++) {\n        const k = Path.Properties[i];\n        clone[k] = this[k];\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    toObject() {\n      const object = super.toObject.call(this);\n      object.width = this.width;\n      object.height = this.height;\n      object.origin = this.origin.toObject();\n      return object;\n    }\n  };\n  var Rectangle = _Rectangle;\n  __publicField(Rectangle, \"Properties\", [\"width\", \"height\"]);\n  var proto12 = {\n    width: {\n      enumerable: true,\n      get: function() {\n        return this._width;\n      },\n      set: function(v) {\n        this._width = v;\n        this._flagWidth = true;\n      }\n    },\n    height: {\n      enumerable: true,\n      get: function() {\n        return this._height;\n      },\n      set: function(v) {\n        this._height = v;\n        this._flagHeight = true;\n      }\n    },\n    origin: {\n      enumerable: true,\n      get: function() {\n        return this._origin;\n      },\n      set: function(v) {\n        if (this._origin) {\n          this._origin.unbind(Events.Types.change, this._renderer.flagVertices);\n        }\n        this._origin = v;\n        this._origin.bind(Events.Types.change, this._renderer.flagVertices);\n        this._renderer.flagVertices();\n      }\n    }\n  };\n\n  // src/effects/sprite.js\n  var _Sprite = class extends Rectangle {\n    _flagTexture = false;\n    _flagColumns = false;\n    _flagRows = false;\n    _flagFrameRate = false;\n    _flagIndex = false;\n    _amount = 1;\n    _duration = 0;\n    _startTime = 0;\n    _playing = false;\n    _firstFrame = 0;\n    _lastFrame = 0;\n    _loop = true;\n    _texture = null;\n    _columns = 1;\n    _rows = 1;\n    _frameRate = 0;\n    _index = 0;\n    _origin = null;\n    constructor(path, ox, oy, cols, rows, frameRate) {\n      super(ox, oy, 0, 0);\n      for (let prop in proto13) {\n        Object.defineProperty(this, prop, proto13[prop]);\n      }\n      this.noStroke();\n      this.noFill();\n      if (path instanceof Texture) {\n        this.texture = path;\n      } else if (typeof path === \"string\") {\n        this.texture = new Texture(path);\n      }\n      this.origin = new Vector();\n      this._update();\n      if (typeof cols === \"number\") {\n        this.columns = cols;\n      }\n      if (typeof rows === \"number\") {\n        this.rows = rows;\n      }\n      if (typeof frameRate === \"number\") {\n        this.frameRate = frameRate;\n      }\n      this.index = 0;\n    }\n    play(firstFrame, lastFrame, onLastFrame) {\n      this._playing = true;\n      this._firstFrame = 0;\n      this._lastFrame = this.amount - 1;\n      this._startTime = _.performance.now();\n      if (typeof firstFrame === \"number\") {\n        this._firstFrame = firstFrame;\n      }\n      if (typeof lastFrame === \"number\") {\n        this._lastFrame = lastFrame;\n      }\n      if (typeof onLastFrame === \"function\") {\n        this._onLastFrame = onLastFrame;\n      } else {\n        delete this._onLastFrame;\n      }\n      if (this._index !== this._firstFrame) {\n        this._startTime -= 1e3 * Math.abs(this._index - this._firstFrame) / this._frameRate;\n      }\n      return this;\n    }\n    pause() {\n      this._playing = false;\n      return this;\n    }\n    stop() {\n      this._playing = false;\n      this._index = 0;\n      return this;\n    }\n    clone(parent) {\n      const clone = new _Sprite(\n        this.texture,\n        this.translation.x,\n        this.translation.y,\n        this.columns,\n        this.rows,\n        this.frameRate\n      );\n      if (this.playing) {\n        clone.play(this._firstFrame, this._lastFrame);\n        clone._loop = this._loop;\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    toObject() {\n      const object = super.toObject.call(this);\n      object.texture = this.texture.toObject();\n      object.columns = this.columns;\n      object.rows = this.rows;\n      object.frameRate = this.frameRate;\n      object.index = this.index;\n      object._firstFrame = this._firstFrame;\n      object._lastFrame = this._lastFrame;\n      object._loop = this._loop;\n      return object;\n    }\n    _update() {\n      const effect = this._texture;\n      const cols = this._columns;\n      const rows = this._rows;\n      let width, height, elapsed, amount, duration;\n      let index, iw, ih, frames;\n      if (effect) {\n        if (this._flagColumns || this._flagRows) {\n          this._amount = this._columns * this._rows;\n        }\n        if (this._flagFrameRate) {\n          this._duration = 1e3 * this._amount / this._frameRate;\n        }\n        if (this._flagTexture) {\n          this.fill = effect;\n        }\n        if (effect.loaded) {\n          iw = effect.image.width;\n          ih = effect.image.height;\n          width = iw / cols;\n          height = ih / rows;\n          amount = this._amount;\n          if (this.width !== width) {\n            this.width = width;\n          }\n          if (this.height !== height) {\n            this.height = height;\n          }\n          if (this._playing && this._frameRate > 0) {\n            if (_.isNaN(this._lastFrame)) {\n              this._lastFrame = amount - 1;\n            }\n            elapsed = _.performance.now() - this._startTime;\n            frames = this._lastFrame + 1;\n            duration = 1e3 * (frames - this._firstFrame) / this._frameRate;\n            if (this._loop) {\n              elapsed = elapsed % duration;\n            } else {\n              elapsed = Math.min(elapsed, duration);\n            }\n            index = lerp(this._firstFrame, frames, elapsed / duration);\n            index = Math.floor(index);\n            if (index !== this._index) {\n              this._index = index;\n              if (index >= this._lastFrame - 1 && this._onLastFrame) {\n                this._onLastFrame();\n              }\n            }\n          }\n          const col = this._index % cols;\n          const row = Math.floor(this._index / cols);\n          const ox = -width * col + (iw - width) / 2;\n          const oy = -height * row + (ih - height) / 2;\n          if (ox !== effect.offset.x) {\n            effect.offset.x = ox;\n          }\n          if (oy !== effect.offset.y) {\n            effect.offset.y = oy;\n          }\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    flagReset() {\n      this._flagTexture = this._flagColumns = this._flagRows = this._flagFrameRate = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var Sprite = _Sprite;\n  __publicField(Sprite, \"Properties\", [\n    \"texture\",\n    \"columns\",\n    \"rows\",\n    \"frameRate\",\n    \"index\"\n  ]);\n  var proto13 = {\n    texture: {\n      enumerable: true,\n      get: function() {\n        return this._texture;\n      },\n      set: function(v) {\n        this._texture = v;\n        this._flagTexture = true;\n      }\n    },\n    columns: {\n      enumerable: true,\n      get: function() {\n        return this._columns;\n      },\n      set: function(v) {\n        this._columns = v;\n        this._flagColumns = true;\n      }\n    },\n    rows: {\n      enumerable: true,\n      get: function() {\n        return this._rows;\n      },\n      set: function(v) {\n        this._rows = v;\n        this._flagRows = true;\n      }\n    },\n    frameRate: {\n      enumerable: true,\n      get: function() {\n        return this._frameRate;\n      },\n      set: function(v) {\n        this._frameRate = v;\n        this._flagFrameRate = true;\n      }\n    },\n    index: {\n      enumerable: true,\n      get: function() {\n        return this._index;\n      },\n      set: function(v) {\n        this._index = v;\n        this._flagIndex = true;\n      }\n    }\n  };\n\n  // src/shapes/circle.js\n  var cos3 = Math.cos;\n  var sin3 = Math.sin;\n  var _Circle = class extends Path {\n    _flagRadius = false;\n    _radius = 0;\n    constructor(ox, oy, r, resolution) {\n      const amount = resolution ? Math.max(resolution, 2) : 4;\n      const points = [];\n      for (let i = 0; i < amount; i++) {\n        points.push(new Anchor(0, 0, 0, 0, 0, 0));\n      }\n      super(points, true, true, true);\n      for (let prop in proto14) {\n        Object.defineProperty(this, prop, proto14[prop]);\n      }\n      if (typeof r === \"number\") {\n        this.radius = r;\n      }\n      this._update();\n      if (typeof ox === \"number\") {\n        this.translation.x = ox;\n      }\n      if (typeof oy === \"number\") {\n        this.translation.y = oy;\n      }\n    }\n    _update() {\n      if (this._flagVertices || this._flagRadius) {\n        let length = this.vertices.length;\n        if (!this._closed && length > 2) {\n          length -= 1;\n        }\n        const c = 4 / 3 * Math.tan(Math.PI / (length * 2));\n        const radius = this._radius;\n        const rc = radius * c;\n        for (let i = 0; i < this.vertices.length; i++) {\n          const pct = i / length;\n          const theta = pct * TWO_PI;\n          const x = radius * cos3(theta);\n          const y = radius * sin3(theta);\n          const lx = rc * cos3(theta - HALF_PI);\n          const ly = rc * sin3(theta - HALF_PI);\n          const rx = rc * cos3(theta + HALF_PI);\n          const ry = rc * sin3(theta + HALF_PI);\n          const v = this.vertices[i];\n          v.command = i === 0 ? Commands.move : Commands.curve;\n          v.set(x, y);\n          v.controls.left.set(lx, ly);\n          v.controls.right.set(rx, ry);\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    flagReset() {\n      this._flagRadius = false;\n      super.flagReset.call(this);\n      return this;\n    }\n    clone(parent) {\n      const clone = new _Circle(0, 0, this.radius, this.vertices.length);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      for (let i = 0; i < Path.Properties.length; i++) {\n        const k = Path.Properties[i];\n        clone[k] = this[k];\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    toObject() {\n      const object = super.toObject.call(this);\n      for (let i = 0; i < _Circle.Properties.length; i++) {\n        const k = _Circle.Properties[i];\n        object[k] = this[k];\n      }\n      return object;\n    }\n  };\n  var Circle = _Circle;\n  __publicField(Circle, \"Properties\", [\"radius\"]);\n  var proto14 = {\n    radius: {\n      enumerable: true,\n      get: function() {\n        return this._radius;\n      },\n      set: function(v) {\n        this._radius = v;\n        this._flagRadius = true;\n      }\n    }\n  };\n\n  // src/shapes/ellipse.js\n  var cos4 = Math.cos;\n  var sin4 = Math.sin;\n  var _Ellipse = class extends Path {\n    _flagWidth = false;\n    _flagHeight = false;\n    _width = 0;\n    _height = 0;\n    constructor(x, y, rx, ry, resolution) {\n      if (typeof ry !== \"number\" && typeof rx === \"number\") {\n        ry = rx;\n      }\n      const amount = resolution ? Math.max(resolution, 2) : 4;\n      const points = [];\n      for (let i = 0; i < amount; i++) {\n        points.push(new Anchor());\n      }\n      super(points, true, true, true);\n      for (let prop in proto15) {\n        Object.defineProperty(this, prop, proto15[prop]);\n      }\n      if (typeof rx === \"number\") {\n        this.width = rx * 2;\n      }\n      if (typeof ry === \"number\") {\n        this.height = ry * 2;\n      }\n      this._update();\n      if (typeof x === \"number\") {\n        this.translation.x = x;\n      }\n      if (typeof y === \"number\") {\n        this.translation.y = y;\n      }\n    }\n    _update() {\n      if (this._flagVertices || this._flagWidth || this._flagHeight) {\n        let length = this.vertices.length;\n        if (!this._closed && length > 2) {\n          length -= 1;\n        }\n        const c = 4 / 3 * Math.tan(Math.PI / (this.vertices.length * 2));\n        const radiusX = this._width / 2;\n        const radiusY = this._height / 2;\n        for (let i = 0; i < this.vertices.length; i++) {\n          const pct = i / length;\n          const theta = pct * TWO_PI;\n          const x = radiusX * cos4(theta);\n          const y = radiusY * sin4(theta);\n          const lx = radiusX * c * cos4(theta - HALF_PI);\n          const ly = radiusY * c * sin4(theta - HALF_PI);\n          const rx = radiusX * c * cos4(theta + HALF_PI);\n          const ry = radiusY * c * sin4(theta + HALF_PI);\n          const v = this.vertices[i];\n          v.command = i === 0 ? Commands.move : Commands.curve;\n          v.set(x, y);\n          v.controls.left.set(lx, ly);\n          v.controls.right.set(rx, ry);\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    flagReset() {\n      this._flagWidth = this._flagHeight = false;\n      super.flagReset.call(this);\n      return this;\n    }\n    clone(parent) {\n      const rx = this.width / 2;\n      const ry = this.height / 2;\n      const resolution = this.vertices.length;\n      const clone = new _Ellipse(0, 0, rx, ry, resolution);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      for (let i = 0; i < Path.Properties.length; i++) {\n        const k = Path.Properties[i];\n        clone[k] = this[k];\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    toObject() {\n      const object = super.toObject.call(this);\n      for (let i = 0; i < _Ellipse.Properties.length; i++) {\n        const k = _Ellipse.Properties[i];\n        object[k] = this[k];\n      }\n      return object;\n    }\n  };\n  var Ellipse = _Ellipse;\n  __publicField(Ellipse, \"Properties\", [\"width\", \"height\"]);\n  var proto15 = {\n    width: {\n      enumerable: true,\n      get: function() {\n        return this._width;\n      },\n      set: function(v) {\n        this._width = v;\n        this._flagWidth = true;\n      }\n    },\n    height: {\n      enumerable: true,\n      get: function() {\n        return this._height;\n      },\n      set: function(v) {\n        this._height = v;\n        this._flagHeight = true;\n      }\n    }\n  };\n\n  // src/shapes/line.js\n  var Line = class extends Path {\n    constructor(x1, y1, x2, y2) {\n      const points = [\n        new Anchor(x1, y1),\n        new Anchor(x2, y2)\n      ];\n      super(points);\n      for (let prop in proto16) {\n        Object.defineProperty(this, prop, proto16[prop]);\n      }\n      this.vertices[0].command = Commands.move;\n      this.vertices[1].command = Commands.line;\n      this.automatic = false;\n    }\n  };\n  var proto16 = {\n    left: {\n      enumerable: true,\n      get: function() {\n        return this.vertices[0];\n      },\n      set: function(v) {\n        if (_.isObject(v)) {\n          this.vertices.splice(0, 1, v);\n        } else {\n          const error = new TwoError(\"Two.Line.x argument is not an object.\");\n          console.warn(error.name, error.message);\n        }\n      }\n    },\n    right: {\n      enumerable: true,\n      get: function() {\n        return this.vertices[1];\n      },\n      set: function(v) {\n        if (_.isObject(v)) {\n          this.vertices.splice(1, 1, v);\n        } else {\n          const error = new TwoError(\"Two.Line.y argument is not an object.\");\n          console.warn(error.name, error.message);\n        }\n      }\n    }\n  };\n\n  // src/shapes/rounded-rectangle.js\n  var _RoundedRectangle = class extends Path {\n    _flagWidth = false;\n    _flagHeight = false;\n    _flagRadius = false;\n    _width = 0;\n    _height = 0;\n    _radius = 12;\n    constructor(x, y, width, height, radius) {\n      if (typeof radius === \"undefined\" && typeof width === \"number\" && typeof height === \"number\") {\n        radius = Math.floor(Math.min(width, height) / 12);\n      }\n      const points = [];\n      for (let i = 0; i < 10; i++) {\n        points.push(\n          new Anchor(\n            0,\n            0,\n            0,\n            0,\n            0,\n            0,\n            i === 0 ? Commands.move : Commands.curve\n          )\n        );\n      }\n      super(points);\n      for (let prop in proto17) {\n        Object.defineProperty(this, prop, proto17[prop]);\n      }\n      this.closed = true;\n      this.automatic = false;\n      this._renderer.flagRadius = FlagRadius.bind(this);\n      if (typeof width === \"number\") {\n        this.width = width;\n      }\n      if (typeof height === \"number\") {\n        this.height = height;\n      }\n      if (typeof radius === \"number\") {\n        this.radius = radius;\n      }\n      this._update();\n      if (typeof x === \"number\") {\n        this.translation.x = x;\n      }\n      if (typeof y === \"number\") {\n        this.translation.y = y;\n      }\n    }\n    _update() {\n      if (this._flagVertices || this._flagWidth || this._flagHeight || this._flagRadius) {\n        const width = this._width;\n        const height = this._height;\n        let rx, ry;\n        if (this._radius instanceof Vector) {\n          rx = this._radius.x;\n          ry = this._radius.y;\n        } else {\n          rx = this._radius;\n          ry = this._radius;\n        }\n        let v;\n        let w = width / 2;\n        let h = height / 2;\n        v = this.vertices[0];\n        v.x = -(w - rx);\n        v.y = -h;\n        v = this.vertices[1];\n        v.x = w - rx;\n        v.y = -h;\n        v.controls.left.clear();\n        v.controls.right.x = rx;\n        v.controls.right.y = 0;\n        v = this.vertices[2];\n        v.x = w;\n        v.y = -(h - ry);\n        v.controls.right.clear();\n        v.controls.left.clear();\n        v = this.vertices[3];\n        v.x = w;\n        v.y = h - ry;\n        v.controls.left.clear();\n        v.controls.right.x = 0;\n        v.controls.right.y = ry;\n        v = this.vertices[4];\n        v.x = w - rx;\n        v.y = h;\n        v.controls.right.clear();\n        v.controls.left.clear();\n        v = this.vertices[5];\n        v.x = -(w - rx);\n        v.y = h;\n        v.controls.left.clear();\n        v.controls.right.x = -rx;\n        v.controls.right.y = 0;\n        v = this.vertices[6];\n        v.x = -w;\n        v.y = h - ry;\n        v.controls.left.clear();\n        v.controls.right.clear();\n        v = this.vertices[7];\n        v.x = -w;\n        v.y = -(h - ry);\n        v.controls.left.clear();\n        v.controls.right.x = 0;\n        v.controls.right.y = -ry;\n        v = this.vertices[8];\n        v.x = -(w - rx);\n        v.y = -h;\n        v.controls.left.clear();\n        v.controls.right.clear();\n        v = this.vertices[9];\n        v.copy(this.vertices[8]);\n      }\n      super._update.call(this);\n      return this;\n    }\n    flagReset() {\n      this._flagWidth = this._flagHeight = this._flagRadius = false;\n      super.flagReset.call(this);\n      return this;\n    }\n    clone(parent) {\n      const width = this.width;\n      const height = this.height;\n      const radius = this.radius;\n      const clone = new _RoundedRectangle(0, 0, width, height, radius);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      for (let i = 0; i < Path.Properties.length; i++) {\n        const k = Path.Properties[i];\n        clone[k] = this[k];\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    toObject() {\n      const object = super.toObject.call(this);\n      for (let i = 0; i < _RoundedRectangle.Properties.length; i++) {\n        const k = _RoundedRectangle.Properties[i];\n        object[k] = this[k];\n      }\n      object.radius = typeof this.radius === \"number\" ? this.radius : this.radius.toObject();\n      return object;\n    }\n  };\n  var RoundedRectangle = _RoundedRectangle;\n  __publicField(RoundedRectangle, \"Properties\", [\"width\", \"height\", \"radius\"]);\n  var proto17 = {\n    width: {\n      enumerable: true,\n      get: function() {\n        return this._width;\n      },\n      set: function(v) {\n        this._width = v;\n        this._flagWidth = true;\n      }\n    },\n    height: {\n      enumerable: true,\n      get: function() {\n        return this._height;\n      },\n      set: function(v) {\n        this._height = v;\n        this._flagHeight = true;\n      }\n    },\n    radius: {\n      enumerable: true,\n      get: function() {\n        return this._radius;\n      },\n      set: function(v) {\n        if (this._radius instanceof Vector) {\n          this._radius.unbind(Events.Types.change, this._renderer.flagRadius);\n        }\n        this._radius = v;\n        if (this._radius instanceof Vector) {\n          this._radius.bind(Events.Types.change, this._renderer.flagRadius);\n        }\n        this._flagRadius = true;\n      }\n    }\n  };\n  function FlagRadius() {\n    this._flagRadius = true;\n  }\n\n  // src/text.js\n  var canvas2;\n  var min4 = Math.min;\n  var max4 = Math.max;\n  if (root.document) {\n    canvas2 = document.createElement(\"canvas\");\n  }\n  var _Text = class extends Shape {\n    _flagValue = true;\n    _flagFamily = true;\n    _flagSize = true;\n    _flagLeading = true;\n    _flagAlignment = true;\n    _flagBaseline = true;\n    _flagStyle = true;\n    _flagWeight = true;\n    _flagDecoration = true;\n    _flagFill = true;\n    _flagStroke = true;\n    _flagLinewidth = true;\n    _flagOpacity = true;\n    _flagVisible = true;\n    _flagMask = false;\n    _flagClip = false;\n    _value = \"\";\n    _family = \"sans-serif\";\n    _size = 13;\n    _leading = 17;\n    _alignment = \"center\";\n    _baseline = \"middle\";\n    _style = \"normal\";\n    _weight = 500;\n    _decoration = \"none\";\n    _fill = \"#000\";\n    _stroke = \"none\";\n    _linewidth = 1;\n    _opacity = 1;\n    _visible = true;\n    _mask = null;\n    _clip = false;\n    _dashes = null;\n    constructor(message, x, y, styles) {\n      super();\n      for (let prop in proto18) {\n        Object.defineProperty(this, prop, proto18[prop]);\n      }\n      this._renderer.type = \"text\";\n      this._renderer.flagFill = FlagFill2.bind(this);\n      this._renderer.flagStroke = FlagStroke2.bind(this);\n      this.value = message;\n      if (typeof x === \"number\") {\n        this.translation.x = x;\n      }\n      if (typeof y === \"number\") {\n        this.translation.y = y;\n      }\n      this.dashes = [];\n      this.dashes.offset = 0;\n      if (!_.isObject(styles)) {\n        return this;\n      }\n      for (let i = 0; i < _Text.Properties.length; i++) {\n        const property = _Text.Properties[i];\n        if (property in styles) {\n          this[property] = styles[property];\n        }\n      }\n    }\n    static Measure(text) {\n      if (canvas2) {\n        const ctx = canvas2.getContext(\"2d\");\n        ctx.font = [\n          text._style,\n          text._weight,\n          `${text._size}px/${text._leading}px`,\n          text._family\n        ].join(\" \");\n        const metrics = ctx.measureText(text.value, 0, 0);\n        const height = metrics.actualBoundingBoxDescent + metrics.actualBoundingBoxAscent;\n        return {\n          width: metrics.width,\n          height\n        };\n      } else {\n        const width = this.value.length * this.size * _Text.Ratio;\n        const height = this.leading;\n        console.warn(\"Two.Text: unable to accurately measure text, so using an approximation.\");\n        return {\n          width,\n          height\n        };\n      }\n    }\n    clone(parent) {\n      const clone = new _Text(this.value);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      for (let i = 0; i < _Text.Properties.length; i++) {\n        const prop = _Text.Properties[i];\n        clone[prop] = this[prop];\n      }\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone._update();\n    }\n    toObject() {\n      const result = {\n        translation: this.translation.toObject(),\n        rotation: this.rotation,\n        scale: this.scale\n      };\n      if (this.matrix.manual) {\n        result.matrix = this.matrix.toObject();\n      }\n      for (let i = 0; i < _Text.Properties.length; i++) {\n        const prop = _Text.Properties[i];\n        result[prop] = this[prop];\n      }\n      return result;\n    }\n    noFill() {\n      this.fill = \"none\";\n      return this;\n    }\n    noStroke() {\n      this.stroke = \"none\";\n      this.linewidth = 0;\n      return this;\n    }\n    getBoundingClientRect(shallow) {\n      let matrix;\n      let left, right, top, bottom;\n      this._update(true);\n      matrix = shallow ? this.matrix : this.worldMatrix;\n      const { width, height } = _Text.Measure(this);\n      const border = (this._linewidth || 0) / 2;\n      switch (this.alignment) {\n        case \"left\":\n          left = -border;\n          right = width + border;\n          break;\n        case \"right\":\n          left = -(width + border);\n          right = border;\n          break;\n        default:\n          left = -(width / 2 + border);\n          right = width / 2 + border;\n      }\n      switch (this.baseline) {\n        case \"middle\":\n          top = -(height / 2 + border);\n          bottom = height / 2 + border;\n          break;\n        default:\n          top = -(height + border);\n          bottom = border;\n      }\n      const [ax, ay] = matrix.multiply(left, top);\n      const [bx, by] = matrix.multiply(left, bottom);\n      const [cx, cy] = matrix.multiply(right, top);\n      const [dx, dy] = matrix.multiply(right, bottom);\n      top = min4(ay, by, cy, dy);\n      left = min4(ax, bx, cx, dx);\n      right = max4(ax, bx, cx, dx);\n      bottom = max4(ay, by, cy, dy);\n      return {\n        top,\n        left,\n        right,\n        bottom,\n        width: right - left,\n        height: bottom - top\n      };\n    }\n    flagReset() {\n      super.flagReset.call(this);\n      this._flagValue = this._flagFamily = this._flagSize = this._flagLeading = this._flagAlignment = this._flagFill = this._flagStroke = this._flagLinewidth = this._flagOpacity = this._flagVisible = this._flagClip = this._flagDecoration = this._flagClassName = this._flagBaseline = this._flagWeight = this._flagStyle = false;\n      return this;\n    }\n  };\n  var Text = _Text;\n  __publicField(Text, \"Ratio\", 0.6);\n  __publicField(Text, \"Properties\", [\n    \"value\",\n    \"family\",\n    \"size\",\n    \"leading\",\n    \"alignment\",\n    \"linewidth\",\n    \"style\",\n    \"weight\",\n    \"decoration\",\n    \"baseline\",\n    \"opacity\",\n    \"visible\",\n    \"fill\",\n    \"stroke\"\n  ]);\n  var proto18 = {\n    value: {\n      enumerable: true,\n      get: function() {\n        return this._value;\n      },\n      set: function(v) {\n        this._value = v;\n        this._flagValue = true;\n      }\n    },\n    family: {\n      enumerable: true,\n      get: function() {\n        return this._family;\n      },\n      set: function(v) {\n        this._family = v;\n        this._flagFamily = true;\n      }\n    },\n    size: {\n      enumerable: true,\n      get: function() {\n        return this._size;\n      },\n      set: function(v) {\n        this._size = v;\n        this._flagSize = true;\n      }\n    },\n    leading: {\n      enumerable: true,\n      get: function() {\n        return this._leading;\n      },\n      set: function(v) {\n        this._leading = v;\n        this._flagLeading = true;\n      }\n    },\n    alignment: {\n      enumerable: true,\n      get: function() {\n        return this._alignment;\n      },\n      set: function(v) {\n        this._alignment = v;\n        this._flagAlignment = true;\n      }\n    },\n    linewidth: {\n      enumerable: true,\n      get: function() {\n        return this._linewidth;\n      },\n      set: function(v) {\n        this._linewidth = v;\n        this._flagLinewidth = true;\n      }\n    },\n    style: {\n      enumerable: true,\n      get: function() {\n        return this._style;\n      },\n      set: function(v) {\n        this._style = v;\n        this._flagStyle = true;\n      }\n    },\n    weight: {\n      enumerable: true,\n      get: function() {\n        return this._weight;\n      },\n      set: function(v) {\n        this._weight = v;\n        this._flagWeight = true;\n      }\n    },\n    decoration: {\n      enumerable: true,\n      get: function() {\n        return this._decoration;\n      },\n      set: function(v) {\n        this._decoration = v;\n        this._flagDecoration = true;\n      }\n    },\n    baseline: {\n      enumerable: true,\n      get: function() {\n        return this._baseline;\n      },\n      set: function(v) {\n        this._baseline = v;\n        this._flagBaseline = true;\n      }\n    },\n    opacity: {\n      enumerable: true,\n      get: function() {\n        return this._opacity;\n      },\n      set: function(v) {\n        this._opacity = v;\n        this._flagOpacity = true;\n      }\n    },\n    visible: {\n      enumerable: true,\n      get: function() {\n        return this._visible;\n      },\n      set: function(v) {\n        this._visible = v;\n        this._flagVisible = true;\n      }\n    },\n    fill: {\n      enumerable: true,\n      get: function() {\n        return this._fill;\n      },\n      set: function(f) {\n        if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n          this._fill.unbind(Events.Types.change, this._renderer.flagFill);\n        }\n        this._fill = f;\n        this._flagFill = true;\n        if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n          this._fill.bind(Events.Types.change, this._renderer.flagFill);\n        }\n      }\n    },\n    stroke: {\n      enumerable: true,\n      get: function() {\n        return this._stroke;\n      },\n      set: function(f) {\n        if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n          this._stroke.unbind(Events.Types.change, this._renderer.flagStroke);\n        }\n        this._stroke = f;\n        this._flagStroke = true;\n        if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n          this._stroke.bind(Events.Types.change, this._renderer.flagStroke);\n        }\n      }\n    },\n    mask: {\n      enumerable: true,\n      get: function() {\n        return this._mask;\n      },\n      set: function(v) {\n        this._mask = v;\n        this._flagMask = true;\n        if (_.isObject(v) && !v.clip) {\n          v.clip = true;\n        }\n      }\n    },\n    clip: {\n      enumerable: true,\n      get: function() {\n        return this._clip;\n      },\n      set: function(v) {\n        this._clip = v;\n        this._flagClip = true;\n      }\n    },\n    dashes: {\n      enumerable: true,\n      get: function() {\n        return this._dashes;\n      },\n      set: function(v) {\n        if (typeof v.offset !== \"number\") {\n          v.offset = this.dashes && this._dashes.offset || 0;\n        }\n        this._dashes = v;\n      }\n    }\n  };\n  function FlagFill2() {\n    this._flagFill = true;\n  }\n  function FlagStroke2() {\n    this._flagStroke = true;\n  }\n\n  // src/utils/interpret-svg.js\n  var regex2 = {\n    path: /[+-]?(?:\\d*\\.\\d+|\\d+)(?:[eE][+-]\\d+)?/g,\n    cssBackgroundImage: /url\\(['\"]?#([\\w\\d-_]*)['\"]?\\)/i,\n    unitSuffix: /[a-zA-Z%]*/i\n  };\n  var alignments = {\n    start: \"left\",\n    middle: \"center\",\n    end: \"right\"\n  };\n  var reservedAttributesToRemove = [\"id\", \"class\", \"transform\", \"xmlns\", \"viewBox\"];\n  var overwriteAttrs = [\"x\", \"y\", \"width\", \"height\", \"href\", \"xlink:href\"];\n  function getAlignment(anchor2) {\n    return alignments[anchor2];\n  }\n  function getBaseline(node) {\n    const a = node.getAttribute(\"dominant-baseline\");\n    const b = node.getAttribute(\"alignment-baseline\");\n    return a || b;\n  }\n  function getTagName(tag) {\n    return tag.replace(/svg:/ig, \"\").toLowerCase();\n  }\n  function applyTransformsToVector(transforms, vector2) {\n    vector2.x += transforms.translateX;\n    vector2.y += transforms.translateY;\n    vector2.x *= transforms.scaleX;\n    vector2.y *= transforms.scaleY;\n    if (transforms.rotation !== 0) {\n      const l = vector2.length();\n      vector2.x = l * Math.cos(transforms.rotation);\n      vector2.y = l * Math.sin(transforms.rotation);\n    }\n  }\n  function extractCSSText(text, styles) {\n    if (!styles) {\n      styles = {};\n    }\n    const commands = text.split(\";\");\n    for (let i = 0; i < commands.length; i++) {\n      const command = commands[i].split(\":\");\n      const name = command[0];\n      const value = command[1];\n      if (typeof name === \"undefined\" || typeof value === \"undefined\") {\n        continue;\n      }\n      styles[name] = value.replace(/\\s/, \"\");\n    }\n    return styles;\n  }\n  function getSvgStyles(node) {\n    const styles = {};\n    const attributes = getSvgAttributes(node);\n    const length = Math.max(attributes.length, node.style.length);\n    for (let i = 0; i < length; i++) {\n      const command = node.style[i];\n      const attribute = attributes[i];\n      if (command) {\n        styles[command] = node.style[command];\n      }\n      if (attribute) {\n        styles[attribute] = node.getAttribute(attribute);\n      }\n    }\n    return styles;\n  }\n  function getSvgAttributes(node) {\n    const attributes = node.getAttributeNames();\n    for (let i = 0; i < reservedAttributesToRemove.length; i++) {\n      const keyword = reservedAttributesToRemove[i];\n      const index = Array.prototype.indexOf.call(attributes, keyword);\n      if (index >= 0) {\n        attributes.splice(index, 1);\n      }\n    }\n    return attributes;\n  }\n  function applySvgViewBox(node, value) {\n    const elements = value.split(/[\\s,]/);\n    const x = -parseFloat(elements[0]);\n    const y = -parseFloat(elements[1]);\n    const width = parseFloat(elements[2]);\n    const height = parseFloat(elements[3]);\n    if (x && y) {\n      for (let i = 0; i < node.children.length; i++) {\n        const child = node.children[i];\n        if (\"translation\" in child) {\n          child.translation.add(x, y);\n        } else if (\"x\" in child) {\n          child.x = x;\n        } else if (\"y\" in child) {\n          child.y = y;\n        }\n      }\n    }\n    const xExists = typeof node.x === \"number\";\n    const yExists = typeof node.y === \"number\";\n    const widthExists = typeof node.width === \"number\";\n    const heightExists = typeof node.height === \"number\";\n    if (xExists) {\n      node.translation.x += node.x;\n    }\n    if (yExists) {\n      node.translation.y += node.y;\n    }\n    if (widthExists || heightExists) {\n      node.scale = new Vector(1, 1);\n    }\n    if (widthExists) {\n      node.scale.x = node.width / width;\n    }\n    if (heightExists) {\n      node.scale.y = node.height / height;\n    }\n    node.mask = new Rectangle(0, 0, width, height);\n    node.mask.origin.set(-width / 2, -height / 2);\n    return node;\n  }\n  function applySvgAttributes(node, elem, parentStyles) {\n    const styles = {}, attributes = {}, extracted = {};\n    let i, m, key, value, prop, attr;\n    let transforms, x, y;\n    let id, scene, ref, tagName;\n    let ca, cb, cc, error;\n    if (node === null) {\n      return styles;\n    }\n    if (root.getComputedStyle) {\n      const computedStyles = root.getComputedStyle(node);\n      i = computedStyles.length;\n      while (i--) {\n        key = computedStyles[i];\n        value = computedStyles[key];\n        if (typeof value !== \"undefined\") {\n          styles[key] = value;\n        }\n      }\n    }\n    for (i = 0; i < node.attributes.length; i++) {\n      attr = node.attributes[i];\n      if (/style/i.test(attr.nodeName)) {\n        extractCSSText(attr.value, extracted);\n      } else {\n        attributes[attr.nodeName] = attr.value;\n      }\n    }\n    if (typeof styles.opacity !== \"undefined\") {\n      styles[\"stroke-opacity\"] = styles.opacity;\n      styles[\"fill-opacity\"] = styles.opacity;\n      delete styles.opacity;\n    }\n    if (parentStyles) {\n      _.defaults(styles, parentStyles);\n    }\n    _.extend(styles, extracted, attributes);\n    styles.visible = !(typeof styles.display === \"undefined\" && /none/i.test(styles.display)) || typeof styles.visibility === \"undefined\" && /hidden/i.test(styles.visibility);\n    for (key in styles) {\n      value = styles[key];\n      switch (key) {\n        case \"gradientTransform\":\n          if (/none/i.test(value))\n            break;\n          m = node.gradientTransform && node.gradientTransform.baseVal && node.gradientTransform.baseVal.length > 0 ? node.gradientTransform.baseVal[0].matrix : node.getCTM ? node.getCTM() : null;\n          if (m === null)\n            break;\n          transforms = decomposeMatrix(m);\n          switch (elem._renderer.type) {\n            case \"linear-gradient\":\n              applyTransformsToVector(transforms, elem.left);\n              applyTransformsToVector(transforms, elem.right);\n              break;\n            case \"radial-gradient\":\n              elem.center.x += transforms.translateX;\n              elem.center.y += transforms.translateY;\n              elem.focal.x += transforms.translateX;\n              elem.focal.y += transforms.translateY;\n              elem.radius *= Math.max(transforms.scaleX, transforms.scaleY);\n              break;\n          }\n          break;\n        case \"transform\":\n          if (/none/i.test(value))\n            break;\n          m = node.transform && node.transform.baseVal && node.transform.baseVal.length > 0 ? node.transform.baseVal[0].matrix : node.getCTM ? node.getCTM() : null;\n          if (m === null)\n            break;\n          if (Constants.AutoCalculateImportedMatrices) {\n            transforms = decomposeMatrix(m);\n            elem.translation.set(transforms.translateX, transforms.translateY);\n            elem.rotation = Math.PI * (transforms.rotation / 180);\n            elem.scale = new Vector(transforms.scaleX, transforms.scaleY);\n            x = parseFloat((styles.x + \"\").replace(\"px\"));\n            y = parseFloat((styles.y + \"\").replace(\"px\"));\n            if (x) {\n              elem.translation.x = x;\n            }\n            if (y) {\n              elem.translation.y = y;\n            }\n          } else {\n            m = node.getCTM();\n            elem._matrix.manual = true;\n            elem._matrix.set(m.a, m.b, m.c, m.d, m.e, m.f);\n          }\n          break;\n        case \"visible\":\n          if (elem instanceof Group) {\n            elem._visible = value;\n            break;\n          }\n          elem.visible = value;\n          break;\n        case \"stroke-linecap\":\n          if (elem instanceof Group) {\n            elem._cap = value;\n            break;\n          }\n          elem.cap = value;\n          break;\n        case \"stroke-linejoin\":\n          if (elem instanceof Group) {\n            elem._join = value;\n            break;\n          }\n          elem.join = value;\n          break;\n        case \"stroke-miterlimit\":\n          if (elem instanceof Group) {\n            elem._miter = value;\n            break;\n          }\n          elem.miter = value;\n          break;\n        case \"stroke-width\":\n          if (elem instanceof Group) {\n            elem._linewidth = parseFloat(value);\n            break;\n          }\n          elem.linewidth = parseFloat(value);\n          break;\n        case \"opacity\":\n        case \"stroke-opacity\":\n        case \"fill-opacity\":\n          if (elem instanceof Group) {\n            elem._opacity = parseFloat(value);\n            break;\n          }\n          elem.opacity = parseFloat(value);\n          break;\n        case \"clip-path\":\n          if (regex2.cssBackgroundImage.test(value)) {\n            id = value.replace(regex2.cssBackgroundImage, \"$1\");\n            if (read.defs.current && read.defs.current.contains(id)) {\n              ref = read.defs.current.get(id);\n              if (ref && ref.childNodes.length > 0) {\n                ref = ref.childNodes[0];\n                tagName = getTagName(ref.nodeName);\n                elem.mask = read[tagName].call(this, ref, {});\n                switch (elem._renderer.type) {\n                  case \"text\":\n                  case \"path\":\n                    elem.position.add(elem.mask.position);\n                    elem.mask.position.clear();\n                    break;\n                }\n              }\n            }\n          }\n          break;\n        case \"fill\":\n        case \"stroke\":\n          prop = (elem instanceof Group ? \"_\" : \"\") + key;\n          if (regex2.cssBackgroundImage.test(value)) {\n            id = value.replace(regex2.cssBackgroundImage, \"$1\");\n            if (read.defs.current && read.defs.current.contains(id)) {\n              ref = read.defs.current.get(id);\n              if (!ref.object) {\n                tagName = getTagName(ref.nodeName);\n                ref.object = read[tagName].call(this, ref, {});\n              }\n              ref = ref.object;\n            } else {\n              scene = getScene(this);\n              ref = scene.getById(id);\n            }\n            elem[prop] = ref;\n          } else {\n            elem[prop] = value;\n          }\n          break;\n        case \"id\":\n          elem.id = value;\n          break;\n        case \"class\":\n        case \"className\":\n          elem.classList = value.split(\" \");\n          elem._flagClassName = true;\n          break;\n        case \"x\":\n        case \"y\":\n          ca = elem instanceof Gradient;\n          cb = elem instanceof LinearGradient;\n          cc = elem instanceof RadialGradient;\n          if (ca || cb || cc) {\n            break;\n          }\n          if (value.match(\"[a-z%]$\") && !value.endsWith(\"px\")) {\n            error = new TwoError(\n              \"only pixel values are supported with the \" + key + \" attribute.\"\n            );\n            console.warn(error.name, error.message);\n          }\n          elem.translation[key] = parseFloat(value);\n          break;\n        case \"font-family\":\n          if (elem instanceof Text) {\n            elem.family = value;\n          }\n          break;\n        case \"font-size\":\n          if (elem instanceof Text) {\n            elem.size = value;\n          }\n          break;\n        case \"font-weight\":\n          if (elem instanceof Text) {\n            elem.weight = value;\n          }\n          break;\n        case \"font-style\":\n          if (elem instanceof Text) {\n            elem.style = value;\n          }\n          break;\n        case \"text-decoration\":\n          if (elem instanceof Text) {\n            elem.decoration = value;\n          }\n          break;\n        case \"line-height\":\n          if (elem instanceof Text) {\n            elem.leading = value;\n          }\n          break;\n      }\n    }\n    if (Object.keys(node.dataset).length)\n      elem.dataset = node.dataset;\n    return styles;\n  }\n  function updateDefsCache(node, defsCache) {\n    for (let i = 0, l = node.childNodes.length; i < l; i++) {\n      const n = node.childNodes[i];\n      if (!n.id)\n        continue;\n      const tagName = getTagName(node.nodeName);\n      if (tagName === \"#text\")\n        continue;\n      defsCache.add(n.id, n);\n    }\n  }\n  function getScene(node) {\n    while (node.parent) {\n      node = node.parent;\n    }\n    return node.scene;\n  }\n  var read = {\n    svg: function(node) {\n      const defs = read.defs.current = new Registry();\n      const elements = node.getElementsByTagName(\"defs\");\n      for (let i = 0; i < elements.length; i++) {\n        updateDefsCache(elements[i], defs);\n      }\n      const svg2 = read.g.call(this, node);\n      const viewBox = node.getAttribute(\"viewBox\");\n      const x = node.getAttribute(\"x\");\n      const y = node.getAttribute(\"y\");\n      const width = node.getAttribute(\"width\");\n      const height = node.getAttribute(\"height\");\n      svg2.defs = defs;\n      const viewBoxExists = viewBox !== null;\n      const xExists = x !== null;\n      const yExists = y !== null;\n      const widthExists = width !== null;\n      const heightExists = height !== null;\n      if (xExists) {\n        svg2.x = parseFloat(x.replace(regex2.unitSuffix, \"\"));\n      }\n      if (yExists) {\n        svg2.y = parseFloat(y.replace(regex2.unitSuffix, \"\"));\n      }\n      if (widthExists) {\n        svg2.width = parseFloat(width.replace(regex2.unitSuffix, \"\"));\n      }\n      if (heightExists) {\n        svg2.height = parseFloat(height.replace(regex2.unitSuffix, \"\"));\n      }\n      if (viewBoxExists) {\n        applySvgViewBox(svg2, viewBox);\n      }\n      delete read.defs.current;\n      return svg2;\n    },\n    defs: function(node) {\n      return null;\n    },\n    use: function(node, styles) {\n      let error;\n      const href = node.getAttribute(\"href\") || node.getAttribute(\"xlink:href\");\n      if (!href) {\n        error = new TwoError(\"encountered <use /> with no href.\");\n        console.warn(error.name, error.message);\n        return null;\n      }\n      const id = href.slice(1);\n      if (!read.defs.current.contains(id)) {\n        error = new TwoError(\n          \"unable to find element for reference \" + href + \".\"\n        );\n        console.warn(error.name, error.message);\n        return null;\n      }\n      const template = read.defs.current.get(id);\n      const fullNode = template.cloneNode(true);\n      for (let i = 0; i < node.attributes.length; i++) {\n        const attr = node.attributes[i];\n        const ca = overwriteAttrs.includes(attr.nodeName);\n        const cb = !fullNode.hasAttribute(attr.nodeName);\n        if (ca || cb) {\n          fullNode.setAttribute(attr.nodeName, attr.value);\n        }\n      }\n      const tagName = getTagName(fullNode.nodeName);\n      return read[tagName].call(this, fullNode, styles);\n    },\n    g: function(node, parentStyles) {\n      const group = new Group();\n      applySvgAttributes.call(this, node, group, parentStyles);\n      this.add(group);\n      const styles = getSvgStyles.call(this, node);\n      for (let i = 0, l = node.childNodes.length; i < l; i++) {\n        const n = node.childNodes[i];\n        const tag = n.nodeName;\n        if (!tag)\n          return;\n        const tagName = getTagName(tag);\n        if (tagName in read) {\n          const o = read[tagName].call(group, n, styles);\n          if (!!o && !o.parent) {\n            group.add(o);\n          }\n        }\n      }\n      return group;\n    },\n    polygon: function(node, parentStyles) {\n      let points;\n      if (typeof node === \"string\") {\n        points = node;\n      } else {\n        points = node.getAttribute(\"points\");\n      }\n      const verts = [];\n      points.replace(/(-?[\\d.eE-]+)[,|\\s](-?[\\d.eE-]+)/g, function(match, p1, p2) {\n        verts.push(new Anchor(parseFloat(p1), parseFloat(p2)));\n      });\n      const poly = new Path(verts, true).noStroke();\n      poly.fill = \"black\";\n      applySvgAttributes.call(this, node, poly, parentStyles);\n      return poly;\n    },\n    polyline: function(node, parentStyles) {\n      const poly = read.polygon.call(this, node, parentStyles);\n      poly.closed = false;\n      return poly;\n    },\n    path: function(node, parentStyles) {\n      let path;\n      if (typeof node === \"string\") {\n        path = node;\n        node = null;\n      } else {\n        path = node.getAttribute(\"d\");\n      }\n      let points = [];\n      let closed2 = false, relative = false;\n      if (path) {\n        let coord = new Anchor();\n        let control, coords;\n        let commands = path.match(/[a-df-z][^a-df-z]*/ig);\n        const last = commands.length - 1;\n        _.each(commands.slice(0), function(command, i) {\n          const items = command.slice(1).trim().match(regex2.path);\n          const type = command[0];\n          const lower = type.toLowerCase();\n          let bin, j, l, ct, times;\n          const result = [];\n          if (i === 0) {\n            commands = [];\n          }\n          switch (lower) {\n            case \"h\":\n            case \"v\":\n              if (items.length > 1) {\n                bin = 1;\n              }\n              break;\n            case \"m\":\n            case \"l\":\n            case \"t\":\n              if (items.length > 2) {\n                bin = 2;\n              }\n              break;\n            case \"s\":\n            case \"q\":\n              if (items.length > 4) {\n                bin = 4;\n              }\n              break;\n            case \"c\":\n              if (items.length > 6) {\n                bin = 6;\n              }\n              break;\n            case \"a\":\n              if (items.length > 7) {\n                bin = 7;\n              }\n              break;\n          }\n          if (bin) {\n            for (j = 0, l = items.length, times = 0; j < l; j += bin) {\n              ct = type;\n              if (times > 0) {\n                switch (type) {\n                  case \"m\":\n                    ct = \"l\";\n                    break;\n                  case \"M\":\n                    ct = \"L\";\n                    break;\n                }\n              }\n              result.push(ct + items.slice(j, j + bin).join(\" \"));\n              times++;\n            }\n            commands = Array.prototype.concat.apply(commands, result);\n          } else {\n            commands.push(command);\n          }\n        });\n        _.each(commands, function(command, i) {\n          let result, x, y;\n          const type = command[0];\n          const lower = type.toLowerCase();\n          coords = command.slice(1).trim().match(regex2.path);\n          relative = type === lower;\n          let x1, y1, x2, y2, x3, y3, x4, y4, reflection;\n          let a, b;\n          let anchor2, rx, ry, xAxisRotation, largeArcFlag, sweepFlag;\n          switch (lower) {\n            case \"z\":\n              if (i >= last) {\n                closed2 = true;\n              } else {\n                x = coord.x;\n                y = coord.y;\n                result = new Anchor(\n                  x,\n                  y,\n                  void 0,\n                  void 0,\n                  void 0,\n                  void 0,\n                  Commands.close\n                );\n                for (let j = points.length - 1; j >= 0; j--) {\n                  const point = points[j];\n                  if (/m/i.test(point.command)) {\n                    coord = point;\n                    break;\n                  }\n                }\n              }\n              break;\n            case \"m\":\n            case \"l\":\n              control = void 0;\n              x = parseFloat(coords[0]);\n              y = parseFloat(coords[1]);\n              result = new Anchor(\n                x,\n                y,\n                void 0,\n                void 0,\n                void 0,\n                void 0,\n                /m/i.test(lower) ? Commands.move : Commands.line\n              );\n              if (relative) {\n                result.addSelf(coord);\n              }\n              coord = result;\n              break;\n            case \"h\":\n            case \"v\":\n              a = /h/i.test(lower) ? \"x\" : \"y\";\n              b = /x/i.test(a) ? \"y\" : \"x\";\n              result = new Anchor(\n                void 0,\n                void 0,\n                void 0,\n                void 0,\n                void 0,\n                void 0,\n                Commands.line\n              );\n              result[a] = parseFloat(coords[0]);\n              result[b] = coord[b];\n              if (relative) {\n                result[a] += coord[a];\n              }\n              coord = result;\n              break;\n            case \"c\":\n            case \"s\":\n              x1 = coord.x;\n              y1 = coord.y;\n              if (!control) {\n                control = new Vector();\n              }\n              if (/c/i.test(lower)) {\n                x2 = parseFloat(coords[0]);\n                y2 = parseFloat(coords[1]);\n                x3 = parseFloat(coords[2]);\n                y3 = parseFloat(coords[3]);\n                x4 = parseFloat(coords[4]);\n                y4 = parseFloat(coords[5]);\n              } else {\n                reflection = getReflection(coord, control, relative);\n                x2 = reflection.x;\n                y2 = reflection.y;\n                x3 = parseFloat(coords[0]);\n                y3 = parseFloat(coords[1]);\n                x4 = parseFloat(coords[2]);\n                y4 = parseFloat(coords[3]);\n              }\n              if (relative) {\n                x2 += x1;\n                y2 += y1;\n                x3 += x1;\n                y3 += y1;\n                x4 += x1;\n                y4 += y1;\n              }\n              coord.controls.right.set(x2 - coord.x, y2 - coord.y);\n              result = new Anchor(\n                x4,\n                y4,\n                x3 - x4,\n                y3 - y4,\n                void 0,\n                void 0,\n                Commands.curve\n              );\n              coord = result;\n              control = result.controls.left;\n              break;\n            case \"t\":\n            case \"q\":\n              x1 = coord.x;\n              y1 = coord.y;\n              if (!control) {\n                control = new Vector();\n              }\n              if (/q/i.test(lower)) {\n                x2 = parseFloat(coords[0]);\n                y2 = parseFloat(coords[1]);\n                x3 = parseFloat(coords[0]);\n                y3 = parseFloat(coords[1]);\n                x4 = parseFloat(coords[2]);\n                y4 = parseFloat(coords[3]);\n              } else {\n                reflection = getReflection(coord, control, relative);\n                x2 = reflection.x;\n                y2 = reflection.y;\n                x3 = reflection.x;\n                y3 = reflection.y;\n                x4 = parseFloat(coords[0]);\n                y4 = parseFloat(coords[1]);\n              }\n              if (relative) {\n                x2 += x1;\n                y2 += y1;\n                x3 += x1;\n                y3 += y1;\n                x4 += x1;\n                y4 += y1;\n              }\n              coord.controls.right.set(\n                (x2 - coord.x) * 0.33,\n                (y2 - coord.y) * 0.33\n              );\n              result = new Anchor(\n                x4,\n                y4,\n                x3 - x4,\n                y3 - y4,\n                void 0,\n                void 0,\n                Commands.curve\n              );\n              coord = result;\n              control = result.controls.left;\n              break;\n            case \"a\":\n              x1 = coord.x;\n              y1 = coord.y;\n              rx = parseFloat(coords[0]);\n              ry = parseFloat(coords[1]);\n              xAxisRotation = parseFloat(coords[2]);\n              largeArcFlag = parseFloat(coords[3]);\n              sweepFlag = parseFloat(coords[4]);\n              x4 = parseFloat(coords[5]);\n              y4 = parseFloat(coords[6]);\n              if (relative) {\n                x4 += x1;\n                y4 += y1;\n              }\n              anchor2 = new Anchor(x4, y4);\n              anchor2.command = Commands.arc;\n              anchor2.rx = rx;\n              anchor2.ry = ry;\n              anchor2.xAxisRotation = xAxisRotation;\n              anchor2.largeArcFlag = largeArcFlag;\n              anchor2.sweepFlag = sweepFlag;\n              result = anchor2;\n              coord = anchor2;\n              control = void 0;\n              break;\n          }\n          if (result) {\n            if (Array.isArray(result)) {\n              points = points.concat(result);\n            } else {\n              points.push(result);\n            }\n          }\n        });\n      }\n      path = new Path(points, closed2, void 0, true).noStroke();\n      path.fill = \"black\";\n      const rect = path.getBoundingClientRect(true);\n      rect.centroid = {\n        x: rect.left + rect.width / 2,\n        y: rect.top + rect.height / 2\n      };\n      _.each(path.vertices, function(v) {\n        v.subSelf(rect.centroid);\n      });\n      applySvgAttributes.call(this, node, path, parentStyles);\n      path.translation.addSelf(rect.centroid);\n      return path;\n    },\n    circle: function(node, parentStyles) {\n      const x = parseFloat(node.getAttribute(\"cx\"));\n      const y = parseFloat(node.getAttribute(\"cy\"));\n      const r = parseFloat(node.getAttribute(\"r\"));\n      const circle = new Circle(0, 0, r).noStroke();\n      circle.fill = \"black\";\n      applySvgAttributes.call(this, node, circle, parentStyles);\n      circle.translation.x = x;\n      circle.translation.y = y;\n      return circle;\n    },\n    ellipse: function(node, parentStyles) {\n      const x = parseFloat(node.getAttribute(\"cx\"));\n      const y = parseFloat(node.getAttribute(\"cy\"));\n      const width = parseFloat(node.getAttribute(\"rx\"));\n      const height = parseFloat(node.getAttribute(\"ry\"));\n      const ellipse = new Ellipse(0, 0, width, height).noStroke();\n      ellipse.fill = \"black\";\n      applySvgAttributes.call(this, node, ellipse, parentStyles);\n      ellipse.translation.x = x;\n      ellipse.translation.y = y;\n      return ellipse;\n    },\n    rect: function(node, parentStyles) {\n      const rx = parseFloat(node.getAttribute(\"rx\"));\n      const ry = parseFloat(node.getAttribute(\"ry\"));\n      if (!_.isNaN(rx) || !_.isNaN(ry)) {\n        return read[\"rounded-rect\"](node);\n      }\n      const width = parseFloat(node.getAttribute(\"width\"));\n      const height = parseFloat(node.getAttribute(\"height\"));\n      const w2 = width / 2;\n      const h2 = height / 2;\n      const rect = new Rectangle(0, 0, width, height).noStroke();\n      rect.fill = \"black\";\n      applySvgAttributes.call(this, node, rect, parentStyles);\n      rect.translation.x += w2;\n      rect.translation.y += h2;\n      return rect;\n    },\n    \"rounded-rect\": function(node, parentStyles) {\n      const rx = parseFloat(node.getAttribute(\"rx\")) || 0;\n      const ry = parseFloat(node.getAttribute(\"ry\")) || 0;\n      const width = parseFloat(node.getAttribute(\"width\"));\n      const height = parseFloat(node.getAttribute(\"height\"));\n      const w2 = width / 2;\n      const h2 = height / 2;\n      const radius = new Vector(rx, ry);\n      const rect = new RoundedRectangle(0, 0, width, height, radius).noStroke();\n      rect.fill = \"black\";\n      applySvgAttributes.call(this, node, rect, parentStyles);\n      rect.translation.x += w2;\n      rect.translation.y += h2;\n      return rect;\n    },\n    line: function(node, parentStyles) {\n      const x1 = parseFloat(node.getAttribute(\"x1\"));\n      const y1 = parseFloat(node.getAttribute(\"y1\"));\n      const x2 = parseFloat(node.getAttribute(\"x2\"));\n      const y2 = parseFloat(node.getAttribute(\"y2\"));\n      const line = new Line(x1, y1, x2, y2).noFill();\n      applySvgAttributes.call(this, node, line, parentStyles);\n      return line;\n    },\n    lineargradient: function(node, parentStyles) {\n      let units = node.getAttribute(\"gradientUnits\");\n      let spread = node.getAttribute(\"spreadMethod\");\n      if (!units) {\n        units = \"objectBoundingBox\";\n      }\n      if (!spread) {\n        spread = \"pad\";\n      }\n      let x1 = parseFloat(node.getAttribute(\"x1\") || 0);\n      let y1 = parseFloat(node.getAttribute(\"y1\") || 0);\n      let x2 = parseFloat(node.getAttribute(\"x2\") || 0);\n      let y2 = parseFloat(node.getAttribute(\"y2\") || 0);\n      const ox = (x2 + x1) / 2;\n      const oy = (y2 + y1) / 2;\n      if (/userSpaceOnUse/i.test(units)) {\n        x1 -= ox;\n        y1 -= oy;\n        x2 -= ox;\n        y2 -= oy;\n      }\n      const stops = [];\n      for (let i = 0; i < node.children.length; i++) {\n        const child = node.children[i];\n        let offset = child.getAttribute(\"offset\");\n        if (/%/ig.test(offset)) {\n          offset = parseFloat(offset.replace(/%/ig, \"\")) / 100;\n        }\n        offset = parseFloat(offset);\n        let color = child.getAttribute(\"stop-color\");\n        let opacity = child.getAttribute(\"stop-opacity\");\n        let style = child.getAttribute(\"style\");\n        let matches;\n        if (color === null) {\n          matches = style ? style.match(/stop-color:\\s?([#a-fA-F0-9]*)/) : false;\n          color = matches && matches.length > 1 ? matches[1] : void 0;\n        }\n        if (opacity === null) {\n          matches = style ? style.match(/stop-opacity:\\s?([0-9.-]*)/) : false;\n          opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1;\n        } else {\n          opacity = parseFloat(opacity);\n        }\n        stops.push(new Stop(offset, color, opacity));\n      }\n      const gradient = new LinearGradient(x1, y1, x2, y2, stops);\n      gradient.spread = spread;\n      gradient.units = units;\n      applySvgAttributes.call(this, node, gradient, parentStyles);\n      return gradient;\n    },\n    radialgradient: function(node, parentStyles) {\n      let units = node.getAttribute(\"gradientUnits\");\n      let spread = node.getAttribute(\"spreadMethod\");\n      if (!units) {\n        units = \"objectBoundingBox\";\n      }\n      if (!spread) {\n        spread = \"pad\";\n      }\n      let cx = parseFloat(node.getAttribute(\"cx\")) || 0;\n      let cy = parseFloat(node.getAttribute(\"cy\")) || 0;\n      let r = parseFloat(node.getAttribute(\"r\"));\n      let fx = parseFloat(node.getAttribute(\"fx\"));\n      let fy = parseFloat(node.getAttribute(\"fy\"));\n      if (_.isNaN(fx)) {\n        fx = cx;\n      }\n      if (_.isNaN(fy)) {\n        fy = cy;\n      }\n      const ox = Math.abs(cx + fx) / 2;\n      const oy = Math.abs(cy + fy) / 2;\n      if (/userSpaceOnUse/i.test(units)) {\n        cx -= ox;\n        cy -= oy;\n        fx -= ox;\n        fy -= oy;\n      }\n      const stops = [];\n      for (let i = 0; i < node.children.length; i++) {\n        const child = node.children[i];\n        let offset = child.getAttribute(\"offset\");\n        if (/%/ig.test(offset)) {\n          offset = parseFloat(offset.replace(/%/ig, \"\")) / 100;\n        }\n        offset = parseFloat(offset);\n        let color = child.getAttribute(\"stop-color\");\n        let opacity = child.getAttribute(\"stop-opacity\");\n        let style = child.getAttribute(\"style\");\n        let matches;\n        if (color === null) {\n          matches = style ? style.match(/stop-color:\\s?([#a-fA-F0-9]*)/) : false;\n          color = matches && matches.length > 1 ? matches[1] : void 0;\n        }\n        if (opacity === null) {\n          matches = style ? style.match(/stop-opacity:\\s?([0-9.-]*)/) : false;\n          opacity = matches && matches.length > 1 ? parseFloat(matches[1]) : 1;\n        } else {\n          opacity = parseFloat(opacity);\n        }\n        stops.push(new Stop(offset, color, opacity));\n      }\n      const gradient = new RadialGradient(cx, cy, r, stops, fx, fy);\n      gradient.spread = spread;\n      gradient.units = units;\n      applySvgAttributes.call(this, node, gradient, parentStyles);\n      return gradient;\n    },\n    text: function(node, parentStyles) {\n      const alignment = getAlignment(node.getAttribute(\"text-anchor\")) || \"left\";\n      const baseline = getBaseline(node) || \"baseline\";\n      const message = node.textContent;\n      const text = new Text(message);\n      applySvgAttributes.call(this, node, text, parentStyles);\n      text.alignment = alignment;\n      text.baseline = baseline;\n      return text;\n    },\n    clippath: function(node, parentStyles) {\n      if (read.defs.current && !read.defs.current.contains(node.id)) {\n        read.defs.current.add(node.id, node);\n      }\n      return null;\n    },\n    image: function(node, parentStyles) {\n      let error;\n      const href = node.getAttribute(\"href\") || node.getAttribute(\"xlink:href\");\n      if (!href) {\n        error = new TwoError(\"encountered <image /> with no href.\");\n        console.warn(error.name, error.message);\n        return null;\n      }\n      const x = parseFloat(node.getAttribute(\"x\")) || 0;\n      const y = parseFloat(node.getAttribute(\"y\")) || 0;\n      const width = parseFloat(node.getAttribute(\"width\"));\n      const height = parseFloat(node.getAttribute(\"height\"));\n      const sprite = new Sprite(href, x, y);\n      if (!_.isNaN(width)) {\n        sprite.width = width;\n      }\n      if (!_.isNaN(height)) {\n        sprite.height = height;\n      }\n      applySvgAttributes.call(this, node, sprite, parentStyles);\n      return sprite;\n    }\n  };\n\n  // src/utils/xhr.js\n  function xhr(path, callback) {\n    const xhr2 = new XMLHttpRequest();\n    xhr2.open(\"GET\", path);\n    xhr2.onreadystatechange = function() {\n      if (xhr2.readyState === 4 && xhr2.status === 200) {\n        callback(xhr2.responseText);\n      }\n    };\n    xhr2.send();\n    return xhr2;\n  }\n\n  // src/effects/image-sequence.js\n  var _ImageSequence = class extends Rectangle {\n    _flagTextures = false;\n    _flagFrameRate = false;\n    _flagIndex = false;\n    _amount = 1;\n    _duration = 0;\n    _index = 0;\n    _startTime = 0;\n    _playing = false;\n    _firstFrame = 0;\n    _lastFrame = 0;\n    _loop = true;\n    _textures = null;\n    _frameRate = 0;\n    _origin = null;\n    constructor(paths, ox, oy, frameRate) {\n      super(ox, oy, 0, 0);\n      for (let prop in proto19) {\n        Object.defineProperty(this, prop, proto19[prop]);\n      }\n      this._renderer.flagTextures = FlagTextures.bind(this);\n      this._renderer.bindTextures = BindTextures.bind(this);\n      this._renderer.unbindTextures = UnbindTextures.bind(this);\n      this.noStroke();\n      this.noFill();\n      if (Array.isArray(paths)) {\n        this.textures = paths.map(GenerateTexture.bind(this));\n      } else {\n        this.textures = [GenerateTexture(paths)];\n      }\n      this.origin = new Vector();\n      this._update();\n      if (typeof frameRate === \"number\") {\n        this.frameRate = frameRate;\n      } else {\n        this.frameRate = _ImageSequence.DefaultFrameRate;\n      }\n      this.index = 0;\n    }\n    play(firstFrame, lastFrame, onLastFrame) {\n      this._playing = true;\n      this._firstFrame = 0;\n      this._lastFrame = this.amount - 1;\n      this._startTime = _.performance.now();\n      if (typeof firstFrame === \"number\") {\n        this._firstFrame = firstFrame;\n      }\n      if (typeof lastFrame === \"number\") {\n        this._lastFrame = lastFrame;\n      }\n      if (typeof onLastFrame === \"function\") {\n        this._onLastFrame = onLastFrame;\n      } else {\n        delete this._onLastFrame;\n      }\n      if (this._index !== this._firstFrame) {\n        this._startTime -= 1e3 * Math.abs(this._index - this._firstFrame) / this._frameRate;\n      }\n      return this;\n    }\n    pause() {\n      this._playing = false;\n      return this;\n    }\n    stop() {\n      this._playing = false;\n      this._index = this._firstFrame;\n      return this;\n    }\n    clone(parent) {\n      const clone = new _ImageSequence(\n        this.textures,\n        this.translation.x,\n        this.translation.y,\n        this.frameRate\n      );\n      clone._loop = this._loop;\n      if (this._playing) {\n        clone.play();\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    toObject() {\n      const object = super.toObject.call(this);\n      object.textures = this.textures.map(function(texture) {\n        return texture.toObject();\n      });\n      object.frameRate = this.frameRate;\n      object.index = this.index;\n      object._firstFrame = this._firstFrame;\n      object._lastFrame = this._lastFrame;\n      object._loop = this._loop;\n      return object;\n    }\n    _update() {\n      const effect = this._textures;\n      let width, height, elapsed, amount, duration, texture;\n      let index, frames;\n      if (effect) {\n        if (this._flagTextures) {\n          this._amount = effect.length;\n        }\n        if (this._flagFrameRate) {\n          this._duration = 1e3 * this._amount / this._frameRate;\n        }\n        if (this._playing && this._frameRate > 0) {\n          amount = this._amount;\n          if (_.isNaN(this._lastFrame)) {\n            this._lastFrame = amount - 1;\n          }\n          elapsed = _.performance.now() - this._startTime;\n          frames = this._lastFrame + 1;\n          duration = 1e3 * (frames - this._firstFrame) / this._frameRate;\n          if (this._loop) {\n            elapsed = elapsed % duration;\n          } else {\n            elapsed = Math.min(elapsed, duration);\n          }\n          index = lerp(this._firstFrame, frames, elapsed / duration);\n          index = Math.floor(index);\n          if (index !== this._index) {\n            this._index = index;\n            texture = effect[this._index];\n            if (texture.loaded) {\n              width = texture.image.width;\n              height = texture.image.height;\n              if (this.width !== width) {\n                this.width = width;\n              }\n              if (this.height !== height) {\n                this.height = height;\n              }\n              this.fill = texture;\n              if (index >= this._lastFrame - 1 && this._onLastFrame) {\n                this._onLastFrame();\n              }\n            }\n          }\n        } else if (this._flagIndex || !(this.fill instanceof Texture)) {\n          texture = effect[this._index];\n          if (texture.loaded) {\n            width = texture.image.width;\n            height = texture.image.height;\n            if (this.width !== width) {\n              this.width = width;\n            }\n            if (this.height !== height) {\n              this.height = height;\n            }\n          }\n          this.fill = texture;\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    flagReset() {\n      this._flagTextures = this._flagFrameRate = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var ImageSequence = _ImageSequence;\n  __publicField(ImageSequence, \"Properties\", [\n    \"textures\",\n    \"frameRate\",\n    \"index\"\n  ]);\n  __publicField(ImageSequence, \"DefaultFrameRate\", 30);\n  var proto19 = {\n    frameRate: {\n      enumerable: true,\n      get: function() {\n        return this._frameRate;\n      },\n      set: function(v) {\n        this._frameRate = v;\n        this._flagFrameRate = true;\n      }\n    },\n    index: {\n      enumerable: true,\n      get: function() {\n        return this._index;\n      },\n      set: function(v) {\n        this._index = v;\n        this._flagIndex = true;\n      }\n    },\n    textures: {\n      enumerable: true,\n      get: function() {\n        return this._textures;\n      },\n      set: function(textures) {\n        const bindTextures = this._renderer.bindTextures;\n        const unbindTextures = this._renderer.unbindTextures;\n        if (this._textures) {\n          this._textures.unbind(Events.Types.insert, bindTextures).unbind(Events.Types.remove, unbindTextures);\n        }\n        this._textures = new Collection((textures || []).slice(0));\n        this._textures.bind(Events.Types.insert, bindTextures).bind(Events.Types.remove, unbindTextures);\n        bindTextures(this._textures);\n      }\n    }\n  };\n  function FlagTextures() {\n    this._flagTextures = true;\n  }\n  function BindTextures(items) {\n    let i = items.length;\n    while (i--) {\n      items[i].bind(Events.Types.change, this._renderer.flagTextures);\n    }\n    this._renderer.flagTextures();\n  }\n  function UnbindTextures(items) {\n    let i = items.length;\n    while (i--) {\n      items[i].unbind(Events.Types.change, this._renderer.flagTextures);\n    }\n    this._renderer.flagTextures();\n  }\n  function GenerateTexture(obj) {\n    if (obj instanceof Texture) {\n      return obj;\n    } else if (typeof obj === \"string\") {\n      return new Texture(obj);\n    }\n  }\n\n  // src/shapes/arc-segment.js\n  var _ArcSegment = class extends Path {\n    _flagStartAngle = false;\n    _flagEndAngle = false;\n    _flagInnerRadius = false;\n    _flagOuterRadius = false;\n    _startAngle = 0;\n    _endAngle = TWO_PI;\n    _innerRadius = 0;\n    _outerRadius = 0;\n    constructor(x, y, ir, or, sa, ea, res) {\n      const amount = res || Constants.Resolution * 3;\n      const points = [];\n      for (let i = 0; i < amount; i++) {\n        points.push(new Anchor());\n      }\n      super(points, true, false, true);\n      for (let prop in proto20) {\n        Object.defineProperty(this, prop, proto20[prop]);\n      }\n      if (typeof ir === \"number\") {\n        this.innerRadius = ir;\n      }\n      if (typeof or === \"number\") {\n        this.outerRadius = or;\n      }\n      if (typeof sa === \"number\") {\n        this.startAngle = sa;\n      }\n      if (typeof ea === \"number\") {\n        this.endAngle = ea;\n      }\n      this._update();\n      if (typeof x === \"number\") {\n        this.translation.x = x;\n      }\n      if (typeof y === \"number\") {\n        this.translation.y = y;\n      }\n    }\n    _update() {\n      if (this._flagVertices || this._flagStartAngle || this._flagEndAngle || this._flagInnerRadius || this._flagOuterRadius) {\n        const sa = this._startAngle;\n        const ea = this._endAngle;\n        const ir = this._innerRadius;\n        const or = this._outerRadius;\n        const connected = mod(sa, TWO_PI) === mod(ea, TWO_PI);\n        const punctured = ir > 0;\n        const vertices = this.vertices;\n        let length = punctured ? vertices.length / 2 : vertices.length;\n        let command, id = 0;\n        let i, last, pct, v, theta, step, x, y, amp;\n        if (connected) {\n          length--;\n        } else if (!punctured) {\n          length -= 2;\n        }\n        for (i = 0, last = length - 1; i < length; i++) {\n          pct = i / last;\n          v = vertices[id];\n          theta = pct * (ea - sa) + sa;\n          step = (ea - sa) / length;\n          x = or * Math.cos(theta);\n          y = or * Math.sin(theta);\n          switch (i) {\n            case 0:\n              command = Commands.move;\n              break;\n            default:\n              command = Commands.curve;\n          }\n          v.command = command;\n          v.x = x;\n          v.y = y;\n          v.controls.left.clear();\n          v.controls.right.clear();\n          if (v.command === Commands.curve) {\n            amp = or * step / Math.PI;\n            v.controls.left.x = amp * Math.cos(theta - HALF_PI);\n            v.controls.left.y = amp * Math.sin(theta - HALF_PI);\n            v.controls.right.x = amp * Math.cos(theta + HALF_PI);\n            v.controls.right.y = amp * Math.sin(theta + HALF_PI);\n            if (i === 1) {\n              v.controls.left.multiplyScalar(2);\n            }\n            if (i === last) {\n              v.controls.right.multiplyScalar(2);\n            }\n          }\n          id++;\n        }\n        if (punctured) {\n          if (connected) {\n            vertices[id].command = Commands.close;\n            id++;\n          } else {\n            length--;\n            last = length - 1;\n          }\n          for (i = 0; i < length; i++) {\n            pct = i / last;\n            v = vertices[id];\n            theta = (1 - pct) * (ea - sa) + sa;\n            step = (ea - sa) / length;\n            x = ir * Math.cos(theta);\n            y = ir * Math.sin(theta);\n            command = Commands.curve;\n            if (i <= 0) {\n              command = connected ? Commands.move : Commands.line;\n            }\n            v.command = command;\n            v.x = x;\n            v.y = y;\n            v.controls.left.clear();\n            v.controls.right.clear();\n            if (v.command === Commands.curve) {\n              amp = ir * step / Math.PI;\n              v.controls.left.x = amp * Math.cos(theta + HALF_PI);\n              v.controls.left.y = amp * Math.sin(theta + HALF_PI);\n              v.controls.right.x = amp * Math.cos(theta - HALF_PI);\n              v.controls.right.y = amp * Math.sin(theta - HALF_PI);\n              if (i === 1) {\n                v.controls.left.multiplyScalar(2);\n              }\n              if (i === last) {\n                v.controls.right.multiplyScalar(2);\n              }\n            }\n            id++;\n          }\n          vertices[id].copy(vertices[0]);\n          vertices[id].command = Commands.line;\n        } else if (!connected) {\n          vertices[id].command = Commands.line;\n          vertices[id].x = 0;\n          vertices[id].y = 0;\n          id++;\n          vertices[id].copy(vertices[0]);\n          vertices[id].command = Commands.line;\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    flagReset() {\n      super.flagReset.call(this);\n      this._flagStartAngle = this._flagEndAngle = this._flagInnerRadius = this._flagOuterRadius = false;\n      return this;\n    }\n    clone(parent) {\n      const ir = this.innerRadius;\n      const or = this.outerRadius;\n      const sa = this.startAngle;\n      const ea = this.endAngle;\n      const resolution = this.vertices.length;\n      const clone = new _ArcSegment(0, 0, ir, or, sa, ea, resolution);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      for (let i = 0; i < Path.Properties.length; i++) {\n        const k = Path.Properties[i];\n        clone[k] = this[k];\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    toObject() {\n      const object = super.toObject.call(this);\n      for (let i = 0; i < _ArcSegment.Properties.length; i++) {\n        const k = _ArcSegment.Properties[i];\n        object[k] = this[k];\n      }\n      return object;\n    }\n  };\n  var ArcSegment = _ArcSegment;\n  __publicField(ArcSegment, \"Properties\", [\"startAngle\", \"endAngle\", \"innerRadius\", \"outerRadius\"]);\n  var proto20 = {\n    startAngle: {\n      enumerable: true,\n      get: function() {\n        return this._startAngle;\n      },\n      set: function(v) {\n        this._startAngle = v;\n        this._flagStartAngle = true;\n      }\n    },\n    endAngle: {\n      enumerable: true,\n      get: function() {\n        return this._endAngle;\n      },\n      set: function(v) {\n        this._endAngle = v;\n        this._flagEndAngle = true;\n      }\n    },\n    innerRadius: {\n      enumerable: true,\n      get: function() {\n        return this._innerRadius;\n      },\n      set: function(v) {\n        this._innerRadius = v;\n        this._flagInnerRadius = true;\n      }\n    },\n    outerRadius: {\n      enumerable: true,\n      get: function() {\n        return this._outerRadius;\n      },\n      set: function(v) {\n        this._outerRadius = v;\n        this._flagOuterRadius = true;\n      }\n    }\n  };\n\n  // src/shapes/points.js\n  var ceil2 = Math.ceil;\n  var floor3 = Math.floor;\n  var _Points = class extends Shape {\n    _flagVertices = true;\n    _flagLength = true;\n    _flagFill = true;\n    _flagStroke = true;\n    _flagLinewidth = true;\n    _flagOpacity = true;\n    _flagVisible = true;\n    _flagSize = true;\n    _flagSizeAttenuation = true;\n    _length = 0;\n    _fill = \"#fff\";\n    _stroke = \"#000\";\n    _linewidth = 1;\n    _opacity = 1;\n    _visible = true;\n    _size = 1;\n    _sizeAttenuation = false;\n    _beginning = 0;\n    _ending = 1;\n    _dashes = null;\n    constructor(vertices) {\n      super();\n      for (let prop in proto21) {\n        Object.defineProperty(this, prop, proto21[prop]);\n      }\n      this._renderer.type = \"points\";\n      this._renderer.flagVertices = FlagVertices.bind(this);\n      this._renderer.bindVertices = BindVertices.bind(this);\n      this._renderer.unbindVertices = UnbindVertices.bind(this);\n      this._renderer.flagFill = FlagFill.bind(this);\n      this._renderer.flagStroke = FlagStroke.bind(this);\n      this._renderer.vertices = null;\n      this._renderer.collection = null;\n      this.sizeAttenuation = false;\n      this.beginning = 0;\n      this.ending = 1;\n      this.fill = \"#fff\";\n      this.stroke = \"#000\";\n      this.className = \"\";\n      this.visible = true;\n      this.vertices = vertices;\n      this.dashes = [];\n      this.dashes.offset = 0;\n    }\n    clone(parent) {\n      const clone = new _Points();\n      for (let j = 0; j < this.vertices.length; j++) {\n        clone.vertices.push(this.vertices[j].clone());\n      }\n      for (let i = 0; i < _Points.Properties.length; i++) {\n        const k = _Points.Properties[i];\n        clone[k] = this[k];\n      }\n      clone.className = this.className;\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone._update();\n    }\n    toObject() {\n      const result = {\n        vertices: this.vertices.map(function(v) {\n          return v.toObject();\n        })\n      };\n      _.each(_Points.Properties, function(k) {\n        result[k] = this[k];\n      }, this);\n      result.className = this.className;\n      result.translation = this.translation.toObject();\n      result.rotation = this.rotation;\n      result.scale = this.scale instanceof Vector ? this.scale.toObject() : this.scale;\n      result.skewX = this.skewX;\n      result.skewY = this.skewY;\n      if (this.matrix.manual) {\n        result.matrix = this.matrix.toObject();\n      }\n      return result;\n    }\n    noFill = Path.prototype.noFill;\n    noStroke = Path.prototype.noStroke;\n    corner = Path.prototype.corner;\n    center = Path.prototype.center;\n    getBoundingClientRect = Path.prototype.getBoundingClientRect;\n    subdivide(limit) {\n      this._update();\n      let points = [];\n      for (let i = 0; i < this.vertices.length; i++) {\n        const a = this.vertices[i];\n        const b = this.vertices[i - 1];\n        if (!b) {\n          continue;\n        }\n        const x1 = a.x;\n        const y1 = a.y;\n        const x2 = b.x;\n        const y2 = b.y;\n        const subdivisions = subdivide(x1, y1, x1, y1, x2, y2, x2, y2, limit);\n        points = points.concat(subdivisions);\n      }\n      this.vertices = points;\n      return this;\n    }\n    _updateLength = Path.prototype._updateLength;\n    _update() {\n      if (this._flagVertices) {\n        if (this._flagLength) {\n          this._updateLength(void 0, true);\n        }\n        const beginning = Math.min(this._beginning, this._ending);\n        const ending = Math.max(this._beginning, this._ending);\n        const bid = getIdByLength(this, beginning * this._length);\n        const eid = getIdByLength(this, ending * this._length);\n        const low = ceil2(bid);\n        const high = floor3(eid);\n        let j = 0, v;\n        this._renderer.vertices = [];\n        this._renderer.collection = [];\n        for (let i = 0; i < this._collection.length; i++) {\n          if (i >= low && i <= high) {\n            v = this._collection[i];\n            this._renderer.collection.push(v);\n            this._renderer.vertices[j * 2 + 0] = v.x;\n            this._renderer.vertices[j * 2 + 1] = v.y;\n            j++;\n          }\n        }\n      }\n      super._update.apply(this, arguments);\n      return this;\n    }\n    flagReset() {\n      this._flagVertices = this._flagLength = this._flagFill = this._flagStroke = this._flagLinewidth = this._flagOpacity = this._flagVisible = this._flagSize = this._flagSizeAttenuation = false;\n      super.flagReset.call(this);\n      return this;\n    }\n  };\n  var Points = _Points;\n  __publicField(Points, \"Properties\", [\n    \"fill\",\n    \"stroke\",\n    \"linewidth\",\n    \"opacity\",\n    \"visible\",\n    \"size\",\n    \"sizeAttenuation\",\n    \"beginning\",\n    \"ending\"\n  ]);\n  var proto21 = {\n    linewidth: {\n      enumerable: true,\n      get: function() {\n        return this._linewidth;\n      },\n      set: function(v) {\n        this._linewidth = v;\n        this._flagLinewidth = true;\n      }\n    },\n    opacity: {\n      enumerable: true,\n      get: function() {\n        return this._opacity;\n      },\n      set: function(v) {\n        this._opacity = v;\n        this._flagOpacity = true;\n      }\n    },\n    visible: {\n      enumerable: true,\n      get: function() {\n        return this._visible;\n      },\n      set: function(v) {\n        this._visible = v;\n        this._flagVisible = true;\n      }\n    },\n    size: {\n      enumerable: true,\n      get: function() {\n        return this._size;\n      },\n      set: function(v) {\n        this._size = v;\n        this._flagSize = true;\n      }\n    },\n    sizeAttenuation: {\n      enumerable: true,\n      get: function() {\n        return this._sizeAttenuation;\n      },\n      set: function(v) {\n        this._sizeAttenuation = v;\n        this._flagSizeAttenuation = true;\n      }\n    },\n    fill: {\n      enumerable: true,\n      get: function() {\n        return this._fill;\n      },\n      set: function(f) {\n        if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n          this._fill.unbind(Events.Types.change, this._renderer.flagFill);\n        }\n        this._fill = f;\n        this._flagFill = true;\n        if (this._fill instanceof Gradient || this._fill instanceof LinearGradient || this._fill instanceof RadialGradient || this._fill instanceof Texture) {\n          this._fill.bind(Events.Types.change, this._renderer.flagFill);\n        }\n      }\n    },\n    stroke: {\n      enumerable: true,\n      get: function() {\n        return this._stroke;\n      },\n      set: function(f) {\n        if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n          this._stroke.unbind(Events.Types.change, this._renderer.flagStroke);\n        }\n        this._stroke = f;\n        this._flagStroke = true;\n        if (this._stroke instanceof Gradient || this._stroke instanceof LinearGradient || this._stroke instanceof RadialGradient || this._stroke instanceof Texture) {\n          this._stroke.bind(Events.Types.change, this._renderer.flagStroke);\n        }\n      }\n    },\n    length: {\n      get: function() {\n        if (this._flagLength) {\n          this._updateLength();\n        }\n        return this._length;\n      }\n    },\n    beginning: {\n      enumerable: true,\n      get: function() {\n        return this._beginning;\n      },\n      set: function(v) {\n        this._beginning = v;\n        this._flagVertices = true;\n      }\n    },\n    ending: {\n      enumerable: true,\n      get: function() {\n        return this._ending;\n      },\n      set: function(v) {\n        this._ending = v;\n        this._flagVertices = true;\n      }\n    },\n    vertices: {\n      enumerable: true,\n      get: function() {\n        return this._collection;\n      },\n      set: function(vertices) {\n        const bindVertices = this._renderer.bindVertices;\n        const unbindVertices = this._renderer.unbindVertices;\n        if (this._collection) {\n          this._collection.unbind(Events.Types.insert, bindVertices).unbind(Events.Types.remove, unbindVertices);\n        }\n        if (vertices instanceof Collection) {\n          this._collection = vertices;\n        } else {\n          this._collection = new Collection(vertices || []);\n        }\n        this._collection.bind(Events.Types.insert, bindVertices).bind(Events.Types.remove, unbindVertices);\n        bindVertices(this._collection);\n      }\n    },\n    dashes: {\n      enumerable: true,\n      get: function() {\n        return this._dashes;\n      },\n      set: function(v) {\n        if (typeof v.offset !== \"number\") {\n          v.offset = this.dashes && this._dashes.offset || 0;\n        }\n        this._dashes = v;\n      }\n    }\n  };\n\n  // src/shapes/polygon.js\n  var cos5 = Math.cos;\n  var sin5 = Math.sin;\n  var _Polygon = class extends Path {\n    _flagWidth = false;\n    _flagHeight = false;\n    _flagSides = false;\n    _radius = 0;\n    _width = 0;\n    _height = 0;\n    _sides = 0;\n    constructor(x, y, radius, sides) {\n      sides = Math.max(sides || 0, 3);\n      super();\n      for (let prop in proto22) {\n        Object.defineProperty(this, prop, proto22[prop]);\n      }\n      this.closed = true;\n      this.automatic = false;\n      if (typeof radius === \"number\") {\n        this.radius = radius;\n      }\n      if (typeof sides === \"number\") {\n        this.sides = sides;\n      }\n      this._update();\n      if (typeof x === \"number\") {\n        this.translation.x = x;\n      }\n      if (typeof y === \"number\") {\n        this.translation.y = y;\n      }\n    }\n    _update() {\n      if (this._flagVertices || this._flagWidth || this._flagHeight || this._flagSides) {\n        const sides = this._sides;\n        const amount = sides + 1;\n        let length = this.vertices.length;\n        if (length > sides) {\n          this.vertices.splice(sides - 1, length - sides);\n          length = sides;\n        }\n        for (let i = 0; i < amount; i++) {\n          const pct = (i + 0.5) / sides;\n          const theta = TWO_PI * pct + Math.PI / 2;\n          const x = this._width * cos5(theta) / 2;\n          const y = this._height * sin5(theta) / 2;\n          if (i >= length) {\n            this.vertices.push(new Anchor(x, y));\n          } else {\n            this.vertices[i].set(x, y);\n          }\n          this.vertices[i].command = i === 0 ? Commands.move : Commands.line;\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    flagReset() {\n      this._flagWidth = this._flagHeight = this._flagSides = false;\n      super.flagReset.call(this);\n      return this;\n    }\n    clone(parent) {\n      const clone = new _Polygon(0, 0, 0, this.sides);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      clone.width = this.width;\n      clone.height = this.height;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      for (let i = 0; i < Path.Properties.length; i++) {\n        const k = Path.Properties[i];\n        clone[k] = this[k];\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    toObject() {\n      const object = super.toObject.call(this);\n      for (let i = 0; i < _Polygon.Properties.length; i++) {\n        const k = _Polygon.Properties[i];\n        object[k] = this[k];\n      }\n      return object;\n    }\n  };\n  var Polygon = _Polygon;\n  __publicField(Polygon, \"Properties\", [\"width\", \"height\", \"sides\"]);\n  var proto22 = {\n    radius: {\n      enumerable: true,\n      get: function() {\n        return this._radius;\n      },\n      set: function(v) {\n        this._radius = v;\n        this.width = v * 2;\n        this.height = v * 2;\n      }\n    },\n    width: {\n      enumerable: true,\n      get: function() {\n        return this._width;\n      },\n      set: function(v) {\n        this._width = v;\n        this._flagWidth = true;\n        this._radius = Math.max(this.width, this.height) / 2;\n      }\n    },\n    height: {\n      enumerable: true,\n      get: function() {\n        return this._height;\n      },\n      set: function(v) {\n        this._height = v;\n        this._flagHeight = true;\n        this._radius = Math.max(this.width, this.height) / 2;\n      }\n    },\n    sides: {\n      enumerable: true,\n      get: function() {\n        return this._sides;\n      },\n      set: function(v) {\n        this._sides = v;\n        this._flagSides = true;\n      }\n    }\n  };\n\n  // src/shapes/star.js\n  var cos6 = Math.cos;\n  var sin6 = Math.sin;\n  var _Star = class extends Path {\n    _flagInnerRadius = false;\n    _flagOuterRadius = false;\n    _flagSides = false;\n    _innerRadius = 0;\n    _outerRadius = 0;\n    _sides = 0;\n    constructor(x, y, innerRadius, outerRadius, sides) {\n      if (arguments.length <= 3) {\n        outerRadius = innerRadius;\n        innerRadius = outerRadius / 2;\n      }\n      if (typeof sides !== \"number\" || sides <= 0) {\n        sides = 5;\n      }\n      super();\n      for (let prop in proto23) {\n        Object.defineProperty(this, prop, proto23[prop]);\n      }\n      this.closed = true;\n      this.automatic = false;\n      if (typeof innerRadius === \"number\") {\n        this.innerRadius = innerRadius;\n      }\n      if (typeof outerRadius === \"number\") {\n        this.outerRadius = outerRadius;\n      }\n      if (typeof sides === \"number\") {\n        this.sides = sides;\n      }\n      this._update();\n      if (typeof x === \"number\") {\n        this.translation.x = x;\n      }\n      if (typeof y === \"number\") {\n        this.translation.y = y;\n      }\n    }\n    _update() {\n      if (this._flagVertices || this._flagInnerRadius || this._flagOuterRadius || this._flagSides) {\n        const sides = this._sides * 2;\n        const amount = sides + 1;\n        let length = this.vertices.length;\n        if (length > sides) {\n          this.vertices.splice(sides - 1, length - sides);\n          length = sides;\n        }\n        for (let i = 0; i < amount; i++) {\n          const pct = (i + 0.5) / sides;\n          const theta = TWO_PI * pct;\n          const r = (!(i % 2) ? this._innerRadius : this._outerRadius) / 2;\n          const x = r * cos6(theta);\n          const y = r * sin6(theta);\n          if (i >= length) {\n            this.vertices.push(new Anchor(x, y));\n          } else {\n            this.vertices[i].set(x, y);\n          }\n          this.vertices[i].command = i === 0 ? Commands.move : Commands.line;\n        }\n      }\n      super._update.call(this);\n      return this;\n    }\n    flagReset() {\n      this._flagInnerRadius = this._flagOuterRadius = this._flagSides = false;\n      super.flagReset.call(this);\n      return this;\n    }\n    clone(parent) {\n      const ir = this.innerRadius;\n      const or = this.outerRadius;\n      const sides = this.sides;\n      const clone = new _Star(0, 0, ir, or, sides);\n      clone.translation.copy(this.translation);\n      clone.rotation = this.rotation;\n      clone.scale = this.scale;\n      clone.skewX = this.skewX;\n      clone.skewY = this.skewY;\n      if (this.matrix.manual) {\n        clone.matrix.copy(this.matrix);\n      }\n      for (let i = 0; i < Path.Properties.length; i++) {\n        const k = Path.Properties[i];\n        clone[k] = this[k];\n      }\n      if (parent) {\n        parent.add(clone);\n      }\n      return clone;\n    }\n    toObject() {\n      const object = super.toObject.call(this);\n      for (let i = 0; i < _Star.Properties.length; i++) {\n        const k = _Star.Properties[i];\n        object[k] = this[k];\n      }\n      return object;\n    }\n  };\n  var Star = _Star;\n  __publicField(Star, \"Properties\", [\"innerRadius\", \"outerRadius\", \"sides\"]);\n  var proto23 = {\n    innerRadius: {\n      enumerable: true,\n      get: function() {\n        return this._innerRadius;\n      },\n      set: function(v) {\n        this._innerRadius = v;\n        this._flagInnerRadius = true;\n      }\n    },\n    outerRadius: {\n      enumerable: true,\n      get: function() {\n        return this._outerRadius;\n      },\n      set: function(v) {\n        this._outerRadius = v;\n        this._flagOuterRadius = true;\n      }\n    },\n    sides: {\n      enumerable: true,\n      get: function() {\n        return this._sides;\n      },\n      set: function(v) {\n        this._sides = v;\n        this._flagSides = true;\n      }\n    }\n  };\n\n  // src/renderers/svg.js\n  var svg = {\n    version: 1.1,\n    ns: \"http://www.w3.org/2000/svg\",\n    xlink: \"http://www.w3.org/1999/xlink\",\n    alignments: {\n      left: \"start\",\n      center: \"middle\",\n      right: \"end\"\n    },\n    createElement: function(name, attrs) {\n      const tag = name;\n      const elem = document.createElementNS(svg.ns, tag);\n      if (tag === \"svg\") {\n        attrs = _.defaults(attrs || {}, {\n          version: svg.version\n        });\n      }\n      if (attrs && Object.keys(attrs).length > 0) {\n        svg.setAttributes(elem, attrs);\n      }\n      return elem;\n    },\n    setAttributes: function(elem, attrs) {\n      const keys = Object.keys(attrs);\n      for (let i = 0; i < keys.length; i++) {\n        if (/href/.test(keys[i])) {\n          elem.setAttributeNS(svg.xlink, keys[i], attrs[keys[i]]);\n        } else {\n          elem.setAttribute(keys[i], attrs[keys[i]]);\n        }\n      }\n      return this;\n    },\n    removeAttributes: function(elem, attrs) {\n      for (let key in attrs) {\n        elem.removeAttribute(key);\n      }\n      return this;\n    },\n    toString: function(points, closed2) {\n      let l = points.length, last = l - 1, d, string = \"\";\n      for (let i = 0; i < l; i++) {\n        const b = points[i];\n        const prev = closed2 ? mod(i - 1, l) : Math.max(i - 1, 0);\n        const a = points[prev];\n        let command, c;\n        let vx, vy, ux, uy, ar, bl, br, cl;\n        let rx, ry, xAxisRotation, largeArcFlag, sweepFlag;\n        let x = toFixed(b.x);\n        let y = toFixed(b.y);\n        switch (b.command) {\n          case Commands.close:\n            command = Commands.close;\n            break;\n          case Commands.arc:\n            rx = b.rx;\n            ry = b.ry;\n            xAxisRotation = b.xAxisRotation;\n            largeArcFlag = b.largeArcFlag;\n            sweepFlag = b.sweepFlag;\n            command = Commands.arc + \" \" + rx + \" \" + ry + \" \" + xAxisRotation + \" \" + largeArcFlag + \" \" + sweepFlag + \" \" + x + \" \" + y;\n            break;\n          case Commands.curve:\n            ar = a.controls && a.controls.right || Vector.zero;\n            bl = b.controls && b.controls.left || Vector.zero;\n            if (a.relative) {\n              vx = toFixed(ar.x + a.x);\n              vy = toFixed(ar.y + a.y);\n            } else {\n              vx = toFixed(ar.x);\n              vy = toFixed(ar.y);\n            }\n            if (b.relative) {\n              ux = toFixed(bl.x + b.x);\n              uy = toFixed(bl.y + b.y);\n            } else {\n              ux = toFixed(bl.x);\n              uy = toFixed(bl.y);\n            }\n            command = (i === 0 ? Commands.move : Commands.curve) + \" \" + vx + \" \" + vy + \" \" + ux + \" \" + uy + \" \" + x + \" \" + y;\n            break;\n          case Commands.move:\n            d = b;\n            command = Commands.move + \" \" + x + \" \" + y;\n            break;\n          default:\n            command = b.command + \" \" + x + \" \" + y;\n        }\n        if (i >= last && closed2) {\n          if (b.command === Commands.curve) {\n            c = d;\n            br = b.controls && b.controls.right || b;\n            cl = c.controls && c.controls.left || c;\n            if (b.relative) {\n              vx = toFixed(br.x + b.x);\n              vy = toFixed(br.y + b.y);\n            } else {\n              vx = toFixed(br.x);\n              vy = toFixed(br.y);\n            }\n            if (c.relative) {\n              ux = toFixed(cl.x + c.x);\n              uy = toFixed(cl.y + c.y);\n            } else {\n              ux = toFixed(cl.x);\n              uy = toFixed(cl.y);\n            }\n            x = toFixed(c.x);\n            y = toFixed(c.y);\n            command += \" C \" + vx + \" \" + vy + \" \" + ux + \" \" + uy + \" \" + x + \" \" + y;\n          }\n          if (b.command !== Commands.close) {\n            command += \" Z\";\n          }\n        }\n        string += command + \" \";\n      }\n      return string;\n    },\n    pointsToString: function(points, size) {\n      let string = \"\";\n      const r = size * 0.5;\n      for (let i = 0; i < points.length; i++) {\n        const x = points[i].x;\n        const y = points[i].y - r;\n        string += Commands.move + \" \" + x + \" \" + y + \" \";\n        string += \"a \" + r + \" \" + r + \" 0 1 0 0.001 0 Z\";\n      }\n      return string;\n    },\n    getClip: function(shape, domElement) {\n      let clip = shape._renderer.clip;\n      if (!clip) {\n        clip = shape._renderer.clip = svg.createElement(\"clipPath\", {\n          \"clip-rule\": \"nonzero\"\n        });\n      }\n      if (clip.parentNode === null) {\n        domElement.defs.appendChild(clip);\n      }\n      return clip;\n    },\n    defs: {\n      update: function(domElement) {\n        const { defs } = domElement;\n        if (defs._flagUpdate) {\n          const children = Array.prototype.slice.call(\n            defs.children,\n            0\n          );\n          for (let i = 0; i < children.length; i++) {\n            const child = children[i];\n            const id = child.id;\n            const selector = `[fill=\"url(#${id})\"],[stroke=\"url(#${id})\"],[clip-path=\"url(#${id})\"]`;\n            const exists = domElement.querySelector(selector);\n            if (!exists) {\n              defs.removeChild(child);\n            }\n          }\n          defs._flagUpdate = false;\n        }\n      }\n    },\n    group: {\n      appendChild: function(object) {\n        const elem = object._renderer.elem;\n        if (!elem) {\n          return;\n        }\n        const tag = elem.nodeName;\n        if (!tag || /(radial|linear)gradient/i.test(tag) || object._clip) {\n          return;\n        }\n        this.elem.appendChild(elem);\n      },\n      removeChild: function(object) {\n        const elem = object._renderer.elem;\n        if (!elem || elem.parentNode != this.elem) {\n          return;\n        }\n        const tag = elem.nodeName;\n        if (!tag) {\n          return;\n        }\n        if (object._clip) {\n          return;\n        }\n        this.elem.removeChild(elem);\n      },\n      orderChild: function(object) {\n        this.elem.appendChild(object._renderer.elem);\n      },\n      renderChild: function(child) {\n        svg[child._renderer.type].render.call(child, this);\n      },\n      render: function(domElement) {\n        if (!this._visible && !this._flagVisible || this._opacity === 0 && !this._flagOpacity) {\n          return this;\n        }\n        this._update();\n        if (!this._renderer.elem) {\n          this._renderer.elem = svg.createElement(\"g\", {\n            id: this.id\n          });\n          domElement.appendChild(this._renderer.elem);\n        }\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        const context = {\n          domElement,\n          elem: this._renderer.elem\n        };\n        if (flagMatrix) {\n          this._renderer.elem.setAttribute(\"transform\", \"matrix(\" + this._matrix.toString() + \")\");\n        }\n        for (let i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          svg[child._renderer.type].render.call(child, domElement);\n        }\n        if (this._flagId) {\n          this._renderer.elem.setAttribute(\"id\", this._id);\n        }\n        if (this._flagOpacity) {\n          this._renderer.elem.setAttribute(\"opacity\", this._opacity);\n        }\n        if (this._flagVisible) {\n          this._renderer.elem.setAttribute(\"display\", this._visible ? \"inline\" : \"none\");\n        }\n        if (this._flagClassName) {\n          this._renderer.elem.setAttribute(\"class\", this.classList.join(\" \"));\n        }\n        if (this._flagAdditions) {\n          this.additions.forEach(svg.group.appendChild, context);\n        }\n        if (this._flagSubtractions) {\n          this.subtractions.forEach(svg.group.removeChild, context);\n        }\n        if (this._flagOrder) {\n          this.children.forEach(svg.group.orderChild, context);\n        }\n        if (this._flagMask) {\n          if (this._mask) {\n            svg[this._mask._renderer.type].render.call(this._mask, domElement);\n            this._renderer.elem.setAttribute(\"clip-path\", \"url(#\" + this._mask.id + \")\");\n          } else {\n            this._renderer.elem.removeAttribute(\"clip-path\");\n          }\n        }\n        if (this.dataset) {\n          Object.assign(this._renderer.elem.dataset, this.dataset);\n        }\n        return this.flagReset();\n      }\n    },\n    path: {\n      render: function(domElement) {\n        if (this._opacity === 0 && !this._flagOpacity) {\n          return this;\n        }\n        this._update();\n        const changed = {};\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        if (flagMatrix) {\n          changed.transform = \"matrix(\" + this._matrix.toString() + \")\";\n        }\n        if (this._flagId) {\n          changed.id = this._id;\n        }\n        if (this._flagVertices) {\n          const vertices = svg.toString(this._renderer.vertices, this._closed);\n          changed.d = vertices;\n        }\n        if (this._fill && this._fill._renderer) {\n          this._renderer.hasFillEffect = true;\n          this._fill._update();\n          svg[this._fill._renderer.type].render.call(this._fill, domElement, true);\n        }\n        if (this._flagFill) {\n          changed.fill = this._fill && this._fill.id ? \"url(#\" + this._fill.id + \")\" : this._fill;\n          if (this._renderer.hasFillEffect && typeof this._fill.id === \"undefined\") {\n            domElement.defs._flagUpdate = true;\n            delete this._renderer.hasFillEffect;\n          }\n        }\n        if (this._stroke && this._stroke._renderer) {\n          this._renderer.hasStrokeEffect = true;\n          this._stroke._update();\n          svg[this._stroke._renderer.type].render.call(this._stroke, domElement, true);\n        }\n        if (this._flagStroke) {\n          changed.stroke = this._stroke && this._stroke.id ? \"url(#\" + this._stroke.id + \")\" : this._stroke;\n          if (this._renderer.hasStrokeEffect && typeof this._stroke.id === \"undefined\") {\n            domElement.defs._flagUpdate = true;\n            delete this._renderer.hasStrokeEffect;\n          }\n        }\n        if (this._flagLinewidth) {\n          changed[\"stroke-width\"] = this._linewidth;\n        }\n        if (this._flagOpacity) {\n          changed[\"stroke-opacity\"] = this._opacity;\n          changed[\"fill-opacity\"] = this._opacity;\n        }\n        if (this._flagClassName) {\n          changed[\"class\"] = this.classList.join(\" \");\n        }\n        if (this._flagVisible) {\n          changed.visibility = this._visible ? \"visible\" : \"hidden\";\n        }\n        if (this._flagCap) {\n          changed[\"stroke-linecap\"] = this._cap;\n        }\n        if (this._flagJoin) {\n          changed[\"stroke-linejoin\"] = this._join;\n        }\n        if (this._flagMiter) {\n          changed[\"stroke-miterlimit\"] = this._miter;\n        }\n        if (this.dashes && this.dashes.length > 0) {\n          changed[\"stroke-dasharray\"] = this.dashes.join(\" \");\n          changed[\"stroke-dashoffset\"] = this.dashes.offset || 0;\n        }\n        if (!this._renderer.elem) {\n          changed.id = this._id;\n          this._renderer.elem = svg.createElement(\"path\", changed);\n          domElement.appendChild(this._renderer.elem);\n        } else {\n          svg.setAttributes(this._renderer.elem, changed);\n        }\n        if (this._flagClip) {\n          const clip = svg.getClip(this, domElement);\n          const elem = this._renderer.elem;\n          if (this._clip) {\n            elem.removeAttribute(\"id\");\n            clip.setAttribute(\"id\", this.id);\n            clip.appendChild(elem);\n          } else {\n            clip.removeAttribute(\"id\");\n            elem.setAttribute(\"id\", this.id);\n            this.parent._renderer.elem.appendChild(elem);\n          }\n        }\n        if (this._flagMask) {\n          if (this._mask) {\n            svg[this._mask._renderer.type].render.call(this._mask, domElement);\n            this._renderer.elem.setAttribute(\"clip-path\", \"url(#\" + this._mask.id + \")\");\n          } else {\n            this._renderer.elem.removeAttribute(\"clip-path\");\n          }\n        }\n        return this.flagReset();\n      }\n    },\n    points: {\n      render: function(domElement) {\n        if (this._opacity === 0 && !this._flagOpacity) {\n          return this;\n        }\n        this._update();\n        const changed = {};\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        if (flagMatrix) {\n          changed.transform = \"matrix(\" + this._matrix.toString() + \")\";\n        }\n        if (this._flagId) {\n          changed.id = this._id;\n        }\n        if (this._flagVertices || this._flagSize || this._flagSizeAttenuation) {\n          let size = this._size;\n          if (!this._sizeAttenuation) {\n            const me = this.worldMatrix.elements;\n            const m = decomposeMatrix(me[0], me[3], me[1], me[4], me[2], me[5]);\n            size /= Math.max(m.scaleX, m.scaleY);\n          }\n          const vertices = svg.pointsToString(this._renderer.collection, size);\n          changed.d = vertices;\n        }\n        if (this._fill && this._fill._renderer) {\n          this._renderer.hasFillEffect = true;\n          this._fill._update();\n          svg[this._fill._renderer.type].render.call(this._fill, domElement, true);\n        }\n        if (this._flagFill) {\n          changed.fill = this._fill && this._fill.id ? \"url(#\" + this._fill.id + \")\" : this._fill;\n          if (this._renderer.hasFillEffect && typeof this._fill.id === \"undefined\") {\n            domElement.defs._flagUpdate = true;\n            delete this._renderer.hasFillEffect;\n          }\n        }\n        if (this._stroke && this._stroke._renderer) {\n          this._renderer.hasStrokeEffect = true;\n          this._stroke._update();\n          svg[this._stroke._renderer.type].render.call(this._stroke, domElement, true);\n        }\n        if (this._flagStroke) {\n          changed.stroke = this._stroke && this._stroke.id ? \"url(#\" + this._stroke.id + \")\" : this._stroke;\n          if (this._renderer.hasStrokeEffect && typeof this._stroke.id === \"undefined\") {\n            domElement.defs._flagUpdate = true;\n            delete this._renderer.hasStrokeEffect;\n          }\n        }\n        if (this._flagLinewidth) {\n          changed[\"stroke-width\"] = this._linewidth;\n        }\n        if (this._flagOpacity) {\n          changed[\"stroke-opacity\"] = this._opacity;\n          changed[\"fill-opacity\"] = this._opacity;\n        }\n        if (this._flagClassName) {\n          changed[\"class\"] = this.classList.join(\" \");\n        }\n        if (this._flagVisible) {\n          changed.visibility = this._visible ? \"visible\" : \"hidden\";\n        }\n        if (this.dashes && this.dashes.length > 0) {\n          changed[\"stroke-dasharray\"] = this.dashes.join(\" \");\n          changed[\"stroke-dashoffset\"] = this.dashes.offset || 0;\n        }\n        if (!this._renderer.elem) {\n          changed.id = this._id;\n          this._renderer.elem = svg.createElement(\"path\", changed);\n          domElement.appendChild(this._renderer.elem);\n        } else {\n          svg.setAttributes(this._renderer.elem, changed);\n        }\n        return this.flagReset();\n      }\n    },\n    text: {\n      render: function(domElement) {\n        this._update();\n        const changed = {};\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        if (flagMatrix) {\n          changed.transform = \"matrix(\" + this._matrix.toString() + \")\";\n        }\n        if (this._flagId) {\n          changed.id = this._id;\n        }\n        if (this._flagFamily) {\n          changed[\"font-family\"] = this._family;\n        }\n        if (this._flagSize) {\n          changed[\"font-size\"] = this._size;\n        }\n        if (this._flagLeading) {\n          changed[\"line-height\"] = this._leading;\n        }\n        if (this._flagAlignment) {\n          changed[\"text-anchor\"] = svg.alignments[this._alignment] || this._alignment;\n        }\n        if (this._flagBaseline) {\n          changed[\"alignment-baseline\"] = changed[\"dominant-baseline\"] = this._baseline;\n        }\n        if (this._flagStyle) {\n          changed[\"font-style\"] = this._style;\n        }\n        if (this._flagWeight) {\n          changed[\"font-weight\"] = this._weight;\n        }\n        if (this._flagDecoration) {\n          changed[\"text-decoration\"] = this._decoration;\n        }\n        if (this._fill && this._fill._renderer) {\n          this._renderer.hasFillEffect = true;\n          this._fill._update();\n          svg[this._fill._renderer.type].render.call(this._fill, domElement, true);\n        }\n        if (this._flagFill) {\n          changed.fill = this._fill && this._fill.id ? \"url(#\" + this._fill.id + \")\" : this._fill;\n          if (this._renderer.hasFillEffect && typeof this._fill.id === \"undefined\") {\n            domElement.defs._flagUpdate = true;\n            delete this._renderer.hasFillEffect;\n          }\n        }\n        if (this._stroke && this._stroke._renderer) {\n          this._renderer.hasStrokeEffect = true;\n          this._stroke._update();\n          svg[this._stroke._renderer.type].render.call(this._stroke, domElement, true);\n        }\n        if (this._flagStroke) {\n          changed.stroke = this._stroke && this._stroke.id ? \"url(#\" + this._stroke.id + \")\" : this._stroke;\n          if (this._renderer.hasStrokeEffect && typeof this._stroke.id === \"undefined\") {\n            domElement.defs._flagUpdate = true;\n            delete this._renderer.hasStrokeEffect;\n          }\n        }\n        if (this._flagLinewidth) {\n          changed[\"stroke-width\"] = this._linewidth;\n        }\n        if (this._flagOpacity) {\n          changed.opacity = this._opacity;\n        }\n        if (this._flagClassName) {\n          changed[\"class\"] = this.classList.join(\" \");\n        }\n        if (this._flagVisible) {\n          changed.visibility = this._visible ? \"visible\" : \"hidden\";\n        }\n        if (this.dashes && this.dashes.length > 0) {\n          changed[\"stroke-dasharray\"] = this.dashes.join(\" \");\n          changed[\"stroke-dashoffset\"] = this.dashes.offset || 0;\n        }\n        if (!this._renderer.elem) {\n          changed.id = this._id;\n          this._renderer.elem = svg.createElement(\"text\", changed);\n          domElement.appendChild(this._renderer.elem);\n        } else {\n          svg.setAttributes(this._renderer.elem, changed);\n        }\n        if (this._flagClip) {\n          const clip = svg.getClip(this, domElement);\n          const elem = this._renderer.elem;\n          if (this._clip) {\n            elem.removeAttribute(\"id\");\n            clip.setAttribute(\"id\", this.id);\n            clip.appendChild(elem);\n          } else {\n            clip.removeAttribute(\"id\");\n            elem.setAttribute(\"id\", this.id);\n            this.parent._renderer.elem.appendChild(elem);\n          }\n        }\n        if (this._flagMask) {\n          if (this._mask) {\n            svg[this._mask._renderer.type].render.call(this._mask, domElement);\n            this._renderer.elem.setAttribute(\"clip-path\", \"url(#\" + this._mask.id + \")\");\n          } else {\n            this._renderer.elem.removeAttribute(\"clip-path\");\n          }\n        }\n        if (this._flagValue) {\n          this._renderer.elem.textContent = this._value;\n        }\n        return this.flagReset();\n      }\n    },\n    \"linear-gradient\": {\n      render: function(domElement, silent) {\n        if (!silent) {\n          this._update();\n        }\n        const changed = {};\n        if (this._flagId) {\n          changed.id = this._id;\n        }\n        if (this._flagEndPoints) {\n          changed.x1 = this.left._x;\n          changed.y1 = this.left._y;\n          changed.x2 = this.right._x;\n          changed.y2 = this.right._y;\n        }\n        if (this._flagSpread) {\n          changed.spreadMethod = this._spread;\n        }\n        if (this._flagUnits) {\n          changed.gradientUnits = this._units;\n        }\n        if (!this._renderer.elem) {\n          changed.id = this._id;\n          this._renderer.elem = svg.createElement(\"linearGradient\", changed);\n        } else {\n          svg.setAttributes(this._renderer.elem, changed);\n        }\n        if (this._renderer.elem.parentNode === null) {\n          domElement.defs.appendChild(this._renderer.elem);\n        }\n        if (this._flagStops) {\n          const lengthChanged = this._renderer.elem.childNodes.length !== this.stops.length;\n          if (lengthChanged) {\n            while (this._renderer.elem.lastChild) {\n              this._renderer.elem.removeChild(this._renderer.elem.lastChild);\n            }\n          }\n          for (let i = 0; i < this.stops.length; i++) {\n            const stop = this.stops[i];\n            const attrs = {};\n            if (stop._flagOffset) {\n              attrs.offset = 100 * stop._offset + \"%\";\n            }\n            if (stop._flagColor) {\n              attrs[\"stop-color\"] = stop._color;\n            }\n            if (stop._flagOpacity) {\n              attrs[\"stop-opacity\"] = stop._opacity;\n            }\n            if (!stop._renderer.elem) {\n              stop._renderer.elem = svg.createElement(\"stop\", attrs);\n            } else {\n              svg.setAttributes(stop._renderer.elem, attrs);\n            }\n            if (lengthChanged) {\n              this._renderer.elem.appendChild(stop._renderer.elem);\n            }\n            stop.flagReset();\n          }\n        }\n        return this.flagReset();\n      }\n    },\n    \"radial-gradient\": {\n      render: function(domElement, silent) {\n        if (!silent) {\n          this._update();\n        }\n        const changed = {};\n        if (this._flagId) {\n          changed.id = this._id;\n        }\n        if (this._flagCenter) {\n          changed.cx = this.center._x;\n          changed.cy = this.center._y;\n        }\n        if (this._flagFocal) {\n          changed.fx = this.focal._x;\n          changed.fy = this.focal._y;\n        }\n        if (this._flagRadius) {\n          changed.r = this._radius;\n        }\n        if (this._flagSpread) {\n          changed.spreadMethod = this._spread;\n        }\n        if (this._flagUnits) {\n          changed.gradientUnits = this._units;\n        }\n        if (!this._renderer.elem) {\n          changed.id = this._id;\n          this._renderer.elem = svg.createElement(\"radialGradient\", changed);\n        } else {\n          svg.setAttributes(this._renderer.elem, changed);\n        }\n        if (this._renderer.elem.parentNode === null) {\n          domElement.defs.appendChild(this._renderer.elem);\n        }\n        if (this._flagStops) {\n          const lengthChanged = this._renderer.elem.childNodes.length !== this.stops.length;\n          if (lengthChanged) {\n            while (this._renderer.elem.lastChild) {\n              this._renderer.elem.removeChild(this._renderer.elem.lastChild);\n            }\n          }\n          for (let i = 0; i < this.stops.length; i++) {\n            const stop = this.stops[i];\n            const attrs = {};\n            if (stop._flagOffset) {\n              attrs.offset = 100 * stop._offset + \"%\";\n            }\n            if (stop._flagColor) {\n              attrs[\"stop-color\"] = stop._color;\n            }\n            if (stop._flagOpacity) {\n              attrs[\"stop-opacity\"] = stop._opacity;\n            }\n            if (!stop._renderer.elem) {\n              stop._renderer.elem = svg.createElement(\"stop\", attrs);\n            } else {\n              svg.setAttributes(stop._renderer.elem, attrs);\n            }\n            if (lengthChanged) {\n              this._renderer.elem.appendChild(stop._renderer.elem);\n            }\n            stop.flagReset();\n          }\n        }\n        return this.flagReset();\n      }\n    },\n    texture: {\n      render: function(domElement, silent) {\n        if (!silent) {\n          this._update();\n        }\n        const changed = {};\n        const styles = { x: 0, y: 0 };\n        const image = this.image;\n        if (this._flagId) {\n          changed.id = this._id;\n        }\n        if (this._flagLoaded && this.loaded) {\n          switch (image.nodeName.toLowerCase()) {\n            case \"canvas\":\n              styles.href = styles[\"xlink:href\"] = image.toDataURL(\"image/png\");\n              break;\n            case \"img\":\n            case \"image\":\n              styles.href = styles[\"xlink:href\"] = this.src;\n              break;\n          }\n        }\n        if (this._flagOffset || this._flagLoaded || this._flagScale) {\n          changed.x = this._offset.x;\n          changed.y = this._offset.y;\n          if (image) {\n            changed.x -= image.width / 2;\n            changed.y -= image.height / 2;\n            if (this._scale instanceof Vector) {\n              changed.x *= this._scale.x;\n              changed.y *= this._scale.y;\n            } else {\n              changed.x *= this._scale;\n              changed.y *= this._scale;\n            }\n          }\n          if (changed.x > 0) {\n            changed.x *= -1;\n          }\n          if (changed.y > 0) {\n            changed.y *= -1;\n          }\n        }\n        if (this._flagScale || this._flagLoaded || this._flagRepeat) {\n          changed.width = 0;\n          changed.height = 0;\n          if (image) {\n            styles.width = changed.width = image.width;\n            styles.height = changed.height = image.height;\n            switch (this._repeat) {\n              case \"no-repeat\":\n                changed.width += 1;\n                changed.height += 1;\n                break;\n            }\n            if (this._scale instanceof Vector) {\n              changed.width *= this._scale.x;\n              changed.height *= this._scale.y;\n            } else {\n              changed.width *= this._scale;\n              changed.height *= this._scale;\n            }\n          }\n        }\n        if (this._flagScale || this._flagLoaded) {\n          if (!this._renderer.image) {\n            this._renderer.image = svg.createElement(\"image\", styles);\n          } else {\n            svg.setAttributes(this._renderer.image, styles);\n          }\n        }\n        if (!this._renderer.elem) {\n          changed.id = this._id;\n          changed.patternUnits = \"userSpaceOnUse\";\n          this._renderer.elem = svg.createElement(\"pattern\", changed);\n        } else if (Object.keys(changed).length !== 0) {\n          svg.setAttributes(this._renderer.elem, changed);\n        }\n        if (this._renderer.elem.parentNode === null) {\n          domElement.defs.appendChild(this._renderer.elem);\n        }\n        if (this._renderer.elem && this._renderer.image && !this._renderer.appended) {\n          this._renderer.elem.appendChild(this._renderer.image);\n          this._renderer.appended = true;\n        }\n        return this.flagReset();\n      }\n    }\n  };\n  var Renderer2 = class extends Events {\n    constructor(params) {\n      super();\n      this.domElement = params.domElement || svg.createElement(\"svg\");\n      this.scene = new Group();\n      this.scene.parent = this;\n      this.defs = svg.createElement(\"defs\");\n      this.defs._flagUpdate = false;\n      this.domElement.appendChild(this.defs);\n      this.domElement.defs = this.defs;\n      this.domElement.style.overflow = \"hidden\";\n    }\n    setSize(width, height) {\n      this.width = width;\n      this.height = height;\n      svg.setAttributes(this.domElement, {\n        width,\n        height\n      });\n      return this.trigger(Events.Types.resize, width, height);\n    }\n    render() {\n      svg.group.render.call(this.scene, this.domElement);\n      svg.defs.update(this.domElement);\n      return this;\n    }\n  };\n  __publicField(Renderer2, \"Utils\", svg);\n\n  // src/utils/shaders.js\n  var shaders = {\n    create: function(gl, source, type) {\n      const shader = gl.createShader(gl[type]);\n      gl.shaderSource(shader, source);\n      gl.compileShader(shader);\n      const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);\n      if (!compiled) {\n        const error = gl.getShaderInfoLog(shader);\n        gl.deleteShader(shader);\n        throw new TwoError(\"unable to compile shader \" + shader + \": \" + error);\n      }\n      return shader;\n    },\n    types: {\n      vertex: \"VERTEX_SHADER\",\n      fragment: \"FRAGMENT_SHADER\"\n    },\n    path: {\n      vertex: `\n      precision mediump float;\n      attribute vec2 a_position;\n\n      uniform mat3 u_matrix;\n      uniform vec2 u_resolution;\n      uniform vec4 u_rect;\n\n      varying vec2 v_textureCoords;\n\n      void main() {\n        vec2 rectCoords = (a_position * (u_rect.zw - u_rect.xy)) + u_rect.xy;\n        vec2 projected = (u_matrix * vec3(rectCoords, 1.0)).xy;\n        vec2 normal = projected / u_resolution;\n        vec2 clipspace = (normal * 2.0) - 1.0;\n\n        gl_Position = vec4(clipspace * vec2(1.0, -1.0), 0.0, 1.0);\n        v_textureCoords = a_position;\n      }\n    `,\n      fragment: `\n      precision mediump float;\n\n      uniform sampler2D u_image;\n      varying vec2 v_textureCoords;\n\n      void main() {\n        vec4 texel = texture2D(u_image, v_textureCoords);\n        if (texel.a == 0.0) {\n          discard;\n        }\n        gl_FragColor = texel;\n      }\n    `\n    },\n    points: {\n      vertex: `\n      precision mediump float;\n      attribute vec2 a_position;\n\n      uniform float u_size;\n      uniform mat3 u_matrix;\n      uniform vec2 u_resolution;\n\n      varying vec2 v_textureCoords;\n\n      void main() {\n        vec2 projected = (u_matrix * vec3(a_position, 1.0)).xy;\n        vec2 normal = projected / u_resolution;\n        vec2 clipspace = (normal * 2.0) - 1.0;\n\n        gl_PointSize = u_size;\n        gl_Position = vec4(clipspace * vec2(1.0, -1.0), 0.0, 1.0);\n        v_textureCoords = a_position;\n      }\n    `,\n      fragment: `\n      precision mediump float;\n\n      uniform sampler2D u_image;\n\n      void main() {\n        vec4 texel = texture2D(u_image, gl_PointCoord);\n        if (texel.a == 0.0) {\n          discard;\n        }\n        gl_FragColor = texel;\n      }\n    `\n    }\n  };\n\n  // src/renderers/webgl.js\n  var multiplyMatrix = Matrix2.Multiply;\n  var identity = [1, 0, 0, 0, 1, 0, 0, 0, 1];\n  var transformation = new NumArray(9);\n  var CanvasUtils = Renderer.Utils;\n  var quad = new NumArray([\n    0,\n    0,\n    1,\n    0,\n    0,\n    1,\n    0,\n    1,\n    1,\n    0,\n    1,\n    1\n  ]);\n  var webgl = {\n    precision: 0.9,\n    isHidden: /(undefined|none|transparent)/i,\n    canvas: root.document ? root.document.createElement(\"canvas\") : { getContext: function() {\n    } },\n    alignments: {\n      left: \"start\",\n      middle: \"center\",\n      right: \"end\"\n    },\n    matrix: new Matrix2(),\n    group: {\n      removeChild: function(child, gl) {\n        if (child.children) {\n          for (let i = 0; i < child.children.length; i++) {\n            webgl.group.removeChild(child.children[i], gl);\n          }\n        }\n        if (child._renderer.texture) {\n          gl.deleteTexture(child._renderer.texture);\n          delete child._renderer.texture;\n        }\n        if (child._renderer.positionBuffer) {\n          gl.deleteBuffer(child._renderer.positionBuffer);\n          delete child._renderer.positionBuffer;\n        }\n      },\n      render: function(gl, programs) {\n        if (!this._visible) {\n          return;\n        }\n        this._update();\n        const parent = this.parent;\n        const flagParentMatrix = parent._matrix && parent._matrix.manual || parent._flagMatrix;\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        if (flagParentMatrix || flagMatrix) {\n          if (!this._renderer.matrix) {\n            this._renderer.matrix = new NumArray(9);\n          }\n          this._matrix.toTransformArray(true, transformation);\n          multiplyMatrix(transformation, parent._renderer.matrix, this._renderer.matrix);\n          if (!(this._renderer.scale instanceof Vector)) {\n            this._renderer.scale = new Vector();\n          }\n          if (this._scale instanceof Vector) {\n            this._renderer.scale.x = this._scale.x;\n            this._renderer.scale.y = this._scale.y;\n          } else {\n            this._renderer.scale.x = this._scale;\n            this._renderer.scale.y = this._scale;\n          }\n          if (!/renderer/i.test(parent._renderer.type)) {\n            this._renderer.scale.x *= parent._renderer.scale.x;\n            this._renderer.scale.y *= parent._renderer.scale.y;\n          }\n          if (flagParentMatrix) {\n            this._flagMatrix = true;\n          }\n        }\n        if (this._mask) {\n          gl.clear(gl.STENCIL_BUFFER_BIT);\n          gl.enable(gl.STENCIL_TEST);\n          gl.stencilFunc(gl.ALWAYS, 1, 0);\n          gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);\n          gl.colorMask(false, false, false, false);\n          webgl[this._mask._renderer.type].render.call(this._mask, gl, programs, this);\n          gl.stencilFunc(gl.EQUAL, 1, 255);\n          gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);\n          gl.colorMask(true, true, true, true);\n        }\n        this._flagOpacity = parent._flagOpacity || this._flagOpacity;\n        this._renderer.opacity = this._opacity * (parent && parent._renderer ? parent._renderer.opacity : 1);\n        let i;\n        if (this._flagSubtractions) {\n          for (i = 0; i < this.subtractions.length; i++) {\n            webgl.group.removeChild(this.subtractions[i], gl);\n          }\n        }\n        for (i = 0; i < this.children.length; i++) {\n          const child = this.children[i];\n          webgl[child._renderer.type].render.call(child, gl, programs);\n        }\n        if (this._mask) {\n          gl.disable(gl.STENCIL_TEST);\n        }\n        return this.flagReset();\n      }\n    },\n    path: {\n      updateCanvas: function(elem) {\n        let prev, a, c, ux, uy, vx, vy, ar, bl, br, cl, x, y;\n        let isOffset;\n        const commands = elem._renderer.vertices;\n        const canvas3 = this.canvas;\n        const ctx = this.ctx;\n        const scale = elem._renderer.scale;\n        const stroke = elem._stroke;\n        const linewidth = elem._linewidth;\n        const fill = elem._fill;\n        const opacity = elem._renderer.opacity || elem._opacity;\n        const cap = elem._cap;\n        const join = elem._join;\n        const miter = elem._miter;\n        const closed2 = elem._closed;\n        const dashes = elem.dashes;\n        const length = commands.length;\n        const last = length - 1;\n        canvas3.width = Math.max(Math.ceil(elem._renderer.rect.width * scale.x), 1);\n        canvas3.height = Math.max(Math.ceil(elem._renderer.rect.height * scale.y), 1);\n        const centroid = elem._renderer.rect.centroid;\n        const cx = centroid.x;\n        const cy = centroid.y;\n        ctx.clearRect(0, 0, canvas3.width, canvas3.height);\n        if (fill) {\n          if (typeof fill === \"string\") {\n            ctx.fillStyle = fill;\n          } else {\n            webgl[fill._renderer.type].render.call(fill, ctx, elem);\n            ctx.fillStyle = fill._renderer.effect;\n          }\n        }\n        if (stroke) {\n          if (typeof stroke === \"string\") {\n            ctx.strokeStyle = stroke;\n          } else {\n            webgl[stroke._renderer.type].render.call(stroke, ctx, elem);\n            ctx.strokeStyle = stroke._renderer.effect;\n          }\n          if (linewidth) {\n            ctx.lineWidth = linewidth;\n          }\n          if (miter) {\n            ctx.miterLimit = miter;\n          }\n          if (join) {\n            ctx.lineJoin = join;\n          }\n          if (!closed2 && cap) {\n            ctx.lineCap = cap;\n          }\n        }\n        if (typeof opacity === \"number\") {\n          ctx.globalAlpha = opacity;\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.lineDashOffset = dashes.offset || 0;\n          ctx.setLineDash(dashes);\n        }\n        let d, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, ax, ay;\n        ctx.save();\n        ctx.scale(scale.x, scale.y);\n        ctx.translate(cx, cy);\n        ctx.beginPath();\n        for (let i = 0; i < commands.length; i++) {\n          const b = commands[i];\n          x = b.x;\n          y = b.y;\n          switch (b.command) {\n            case Commands.close:\n              ctx.closePath();\n              break;\n            case Commands.arc:\n              rx = b.rx;\n              ry = b.ry;\n              xAxisRotation = b.xAxisRotation;\n              largeArcFlag = b.largeArcFlag;\n              sweepFlag = b.sweepFlag;\n              prev = closed2 ? mod(i - 1, length) : Math.max(i - 1, 0);\n              a = commands[prev];\n              ax = a.x;\n              ay = a.y;\n              CanvasUtils.renderSvgArcCommand(ctx, ax, ay, rx, ry, largeArcFlag, sweepFlag, xAxisRotation, x, y);\n              break;\n            case Commands.curve:\n              prev = closed2 ? mod(i - 1, length) : Math.max(i - 1, 0);\n              a = commands[prev];\n              ar = a.controls && a.controls.right || Vector.zero;\n              bl = b.controls && b.controls.left || Vector.zero;\n              if (a._relative) {\n                vx = ar.x + a.x;\n                vy = ar.y + a.y;\n              } else {\n                vx = ar.x;\n                vy = ar.y;\n              }\n              if (b._relative) {\n                ux = bl.x + b.x;\n                uy = bl.y + b.y;\n              } else {\n                ux = bl.x;\n                uy = bl.y;\n              }\n              ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n              if (i >= last && closed2) {\n                c = d;\n                br = b.controls && b.controls.right || Vector.zero;\n                cl = c.controls && c.controls.left || Vector.zero;\n                if (b._relative) {\n                  vx = br.x + b.x;\n                  vy = br.y + b.y;\n                } else {\n                  vx = br.x;\n                  vy = br.y;\n                }\n                if (c._relative) {\n                  ux = cl.x + c.x;\n                  uy = cl.y + c.y;\n                } else {\n                  ux = cl.x;\n                  uy = cl.y;\n                }\n                x = c.x;\n                y = c.y;\n                ctx.bezierCurveTo(vx, vy, ux, uy, x, y);\n              }\n              break;\n            case Commands.line:\n              ctx.lineTo(x, y);\n              break;\n            case Commands.move:\n              d = b;\n              ctx.moveTo(x, y);\n              break;\n          }\n        }\n        if (closed2) {\n          ctx.closePath();\n        }\n        if (!webgl.isHidden.test(fill)) {\n          isOffset = fill._renderer && fill._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(\n              -fill._renderer.offset.x,\n              -fill._renderer.offset.y\n            );\n            ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n          }\n          ctx.fill();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n        if (!webgl.isHidden.test(stroke)) {\n          isOffset = stroke._renderer && stroke._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(\n              -stroke._renderer.offset.x,\n              -stroke._renderer.offset.y\n            );\n            ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n            ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n          }\n          ctx.stroke();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n        ctx.restore();\n      },\n      getBoundingClientRect: function(vertices, border, rect) {\n        let left = Infinity, right = -Infinity, top = Infinity, bottom = -Infinity, width, height;\n        vertices.forEach(function(v) {\n          const x = v.x, y = v.y, controls = v.controls;\n          let a, b, c, d, cl, cr;\n          top = Math.min(y, top);\n          left = Math.min(x, left);\n          right = Math.max(x, right);\n          bottom = Math.max(y, bottom);\n          if (!v.controls) {\n            return;\n          }\n          cl = controls.left;\n          cr = controls.right;\n          if (!cl || !cr) {\n            return;\n          }\n          a = v._relative ? cl.x + x : cl.x;\n          b = v._relative ? cl.y + y : cl.y;\n          c = v._relative ? cr.x + x : cr.x;\n          d = v._relative ? cr.y + y : cr.y;\n          if (!a || !b || !c || !d) {\n            return;\n          }\n          top = Math.min(b, d, top);\n          left = Math.min(a, c, left);\n          right = Math.max(a, c, right);\n          bottom = Math.max(b, d, bottom);\n        });\n        if (typeof border === \"number\") {\n          top -= border;\n          left -= border;\n          right += border;\n          bottom += border;\n        }\n        width = right - left;\n        height = bottom - top;\n        rect.top = top;\n        rect.left = left;\n        rect.right = right;\n        rect.bottom = bottom;\n        rect.width = width;\n        rect.height = height;\n        if (!rect.centroid) {\n          rect.centroid = {};\n        }\n        rect.centroid.x = -left;\n        rect.centroid.y = -top;\n      },\n      render: function(gl, programs, forcedParent) {\n        if (!this._visible || !this._opacity) {\n          return this;\n        }\n        this._update();\n        const parent = forcedParent || this.parent;\n        const program = programs[this._renderer.type];\n        const flagParentMatrix = parent._matrix.manual || parent._flagMatrix;\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        const parentChanged = this._renderer.parent !== parent;\n        const flagTexture = this._flagVertices || this._flagFill || this._fill instanceof LinearGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagEndPoints) || this._fill instanceof RadialGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagRadius || this._fill._flagCenter || this._fill._flagFocal) || this._fill instanceof Texture && (this._fill._flagLoaded && this._fill.loaded || this._fill._flagImage || this._fill._flagVideo || this._fill._flagRepeat || this._fill._flagOffset || this._fill._flagScale) || this._stroke instanceof LinearGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagEndPoints) || this._stroke instanceof RadialGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagRadius || this._stroke._flagCenter || this._stroke._flagFocal) || this._stroke instanceof Texture && (this._stroke._flagLoaded && this._stroke.loaded || this._stroke._flagImage || this._stroke._flagVideo || this._stroke._flagRepeat || this._stroke._flagOffset || this._fill._flagScale) || this._flagStroke || this._flagLinewidth || this._flagOpacity || parent._flagOpacity || this._flagVisible || this._flagCap || this._flagJoin || this._flagMiter || this._flagScale || this.dashes && this.dashes.length > 0 || !this._renderer.texture;\n        if (flagParentMatrix || flagMatrix || parentChanged) {\n          if (!this._renderer.matrix) {\n            this._renderer.matrix = new NumArray(9);\n          }\n          this._matrix.toTransformArray(true, transformation);\n          multiplyMatrix(transformation, parent._renderer.matrix, this._renderer.matrix);\n          if (!(this._renderer.scale instanceof Vector)) {\n            this._renderer.scale = new Vector();\n          }\n          if (this._scale instanceof Vector) {\n            this._renderer.scale.x = this._scale.x * parent._renderer.scale.x;\n            this._renderer.scale.y = this._scale.y * parent._renderer.scale.y;\n          } else {\n            this._renderer.scale.x = this._scale * parent._renderer.scale.x;\n            this._renderer.scale.y = this._scale * parent._renderer.scale.y;\n          }\n          if (parentChanged) {\n            this._renderer.parent = parent;\n          }\n        }\n        if (this._mask) {\n          gl.clear(gl.STENCIL_BUFFER_BIT);\n          gl.enable(gl.STENCIL_TEST);\n          gl.stencilFunc(gl.ALWAYS, 1, 0);\n          gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);\n          gl.colorMask(false, false, false, false);\n          webgl[this._mask._renderer.type].render.call(this._mask, gl, programs, this);\n          gl.stencilFunc(gl.EQUAL, 1, 255);\n          gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);\n          gl.colorMask(true, true, true, true);\n        }\n        if (flagTexture) {\n          if (!this._renderer.rect) {\n            this._renderer.rect = {};\n          }\n          this._renderer.opacity = this._opacity * parent._renderer.opacity;\n          webgl.path.getBoundingClientRect(\n            this._renderer.vertices,\n            this._linewidth,\n            this._renderer.rect\n          );\n          webgl.updateTexture.call(webgl, gl, this);\n        } else {\n          if (this._fill && this._fill._update) {\n            this._fill._update();\n          }\n          if (this._stroke && this._stroke._update) {\n            this._stroke._update();\n          }\n        }\n        if (this._clip && !forcedParent || !this._renderer.texture) {\n          return this;\n        }\n        if (programs.current !== program) {\n          gl.useProgram(program);\n          gl.bindBuffer(gl.ARRAY_BUFFER, programs.buffers.position);\n          gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n          gl.enableVertexAttribArray(program.position);\n          gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n          if (!programs.resolution.flagged) {\n            gl.uniform2f(\n              gl.getUniformLocation(program, \"u_resolution\"),\n              programs.resolution.width,\n              programs.resolution.height\n            );\n          }\n          programs.current = program;\n        }\n        if (programs.resolution.flagged) {\n          gl.uniform2f(\n            gl.getUniformLocation(program, \"u_resolution\"),\n            programs.resolution.width,\n            programs.resolution.height\n          );\n        }\n        gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture);\n        const rect = this._renderer.rect;\n        gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix);\n        gl.uniform4f(program.rect, rect.left, rect.top, rect.right, rect.bottom);\n        gl.drawArrays(gl.TRIANGLES, 0, 6);\n        if (this._mask) {\n          gl.disable(gl.STENCIL_TEST);\n        }\n        return this.flagReset();\n      }\n    },\n    points: {\n      updateCanvas: function(elem) {\n        let isOffset;\n        const canvas3 = this.canvas;\n        const ctx = this.ctx;\n        const stroke = elem._stroke;\n        const linewidth = elem._linewidth;\n        const fill = elem._fill;\n        const opacity = elem._renderer.opacity || elem._opacity;\n        const dashes = elem.dashes;\n        const size = elem._size;\n        let dimension = size;\n        if (!webgl.isHidden.test(stroke)) {\n          dimension += linewidth;\n        }\n        canvas3.width = getPoT(dimension);\n        canvas3.height = canvas3.width;\n        const aspect = dimension / canvas3.width;\n        const cx = canvas3.width / 2;\n        const cy = canvas3.height / 2;\n        ctx.clearRect(0, 0, canvas3.width, canvas3.height);\n        if (fill) {\n          if (typeof fill === \"string\") {\n            ctx.fillStyle = fill;\n          } else {\n            webgl[fill._renderer.type].render.call(fill, ctx, elem);\n            ctx.fillStyle = fill._renderer.effect;\n          }\n        }\n        if (stroke) {\n          if (typeof stroke === \"string\") {\n            ctx.strokeStyle = stroke;\n          } else {\n            webgl[stroke._renderer.type].render.call(stroke, ctx, elem);\n            ctx.strokeStyle = stroke._renderer.effect;\n          }\n          if (linewidth) {\n            ctx.lineWidth = linewidth / aspect;\n          }\n        }\n        if (typeof opacity === \"number\") {\n          ctx.globalAlpha = opacity;\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.lineDashOffset = dashes.offset || 0;\n          ctx.setLineDash(dashes);\n        }\n        ctx.save();\n        ctx.translate(cx, cy);\n        ctx.scale(webgl.precision, webgl.precision);\n        ctx.beginPath();\n        ctx.arc(0, 0, size / aspect * 0.5, 0, TWO_PI);\n        ctx.restore();\n        if (closed) {\n          ctx.closePath();\n        }\n        if (!webgl.isHidden.test(fill)) {\n          isOffset = fill._renderer && fill._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(\n              -fill._renderer.offset.x,\n              -fill._renderer.offset.y\n            );\n            ctx.scale(fill._renderer.scale.x, fill._renderer.scale.y);\n          }\n          ctx.fill();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n        if (!webgl.isHidden.test(stroke)) {\n          isOffset = stroke._renderer && stroke._renderer.offset;\n          if (isOffset) {\n            ctx.save();\n            ctx.translate(\n              -stroke._renderer.offset.x,\n              -stroke._renderer.offset.y\n            );\n            ctx.scale(stroke._renderer.scale.x, stroke._renderer.scale.y);\n            ctx.lineWidth = linewidth / stroke._renderer.scale.x;\n          }\n          ctx.stroke();\n          if (isOffset) {\n            ctx.restore();\n          }\n        }\n      },\n      render: function(gl, programs, forcedParent) {\n        if (!this._visible || !this._opacity) {\n          return this;\n        }\n        this._update();\n        let size = this._size;\n        const parent = forcedParent || this.parent;\n        const program = programs[this._renderer.type];\n        const sizeAttenuation = this._sizeAttenuation;\n        const stroke = this._stroke;\n        const linewidth = this._linewidth;\n        const flagParentMatrix = parent._matrix.manual || parent._flagMatrix;\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        const parentChanged = this._renderer.parent !== parent;\n        const commands = this._renderer.vertices;\n        const length = this._renderer.collection.length;\n        const flagVertices = this._flagVertices;\n        const flagTexture = this._flagFill || this._fill instanceof LinearGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagEndPoints) || this._fill instanceof RadialGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagRadius || this._fill._flagCenter || this._fill._flagFocal) || this._fill instanceof Texture && (this._fill._flagLoaded && this._fill.loaded || this._fill._flagImage || this._fill._flagVideo || this._fill._flagRepeat || this._fill._flagOffset || this._fill._flagScale) || this._stroke instanceof LinearGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagEndPoints) || this._stroke instanceof RadialGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagRadius || this._stroke._flagCenter || this._stroke._flagFocal) || this._stroke instanceof Texture && (this._stroke._flagLoaded && this._stroke.loaded || this._stroke._flagImage || this._stroke._flagVideo || this._stroke._flagRepeat || this._stroke._flagOffset || this._fill._flagScale) || this._flagStroke || this._flagLinewidth || this._flagOpacity || parent._flagOpacity || this._flagVisible || this._flagScale || this.dashes && this.dashes.length > 0 || !this._renderer.texture;\n        if (flagParentMatrix || flagMatrix || parentChanged) {\n          if (!this._renderer.matrix) {\n            this._renderer.matrix = new NumArray(9);\n          }\n          this._matrix.toTransformArray(true, transformation);\n          multiplyMatrix(transformation, parent._renderer.matrix, this._renderer.matrix);\n          if (!(this._renderer.scale instanceof Vector)) {\n            this._renderer.scale = new Vector();\n          }\n          if (this._scale instanceof Vector) {\n            this._renderer.scale.x = this._scale.x * parent._renderer.scale.x;\n            this._renderer.scale.y = this._scale.y * parent._renderer.scale.y;\n          } else {\n            this._renderer.scale.x = this._scale * parent._renderer.scale.x;\n            this._renderer.scale.y = this._scale * parent._renderer.scale.y;\n          }\n          if (parentChanged) {\n            this._renderer.parent = parent;\n          }\n        }\n        if (flagVertices) {\n          const positionBuffer = this._renderer.positionBuffer;\n          if (positionBuffer) {\n            gl.deleteBuffer(positionBuffer);\n          }\n          this._renderer.positionBuffer = gl.createBuffer();\n          gl.bindBuffer(gl.ARRAY_BUFFER, this._renderer.positionBuffer);\n          gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n          gl.enableVertexAttribArray(program.position);\n          gl.bufferData(gl.ARRAY_BUFFER, commands, gl.STATIC_DRAW);\n        }\n        if (flagTexture) {\n          this._renderer.opacity = this._opacity * parent._renderer.opacity;\n          webgl.updateTexture.call(webgl, gl, this);\n        } else {\n          if (this._fill && this._fill._update) {\n            this._fill._update();\n          }\n          if (this._stroke && this._stroke._update) {\n            this._stroke._update();\n          }\n        }\n        if (this._clip && !forcedParent || !this._renderer.texture) {\n          return this;\n        }\n        if (!webgl.isHidden.test(stroke)) {\n          size += linewidth;\n        }\n        size /= webgl.precision;\n        if (sizeAttenuation) {\n          size *= Math.max(this._renderer.scale.x, this._renderer.scale.y);\n        }\n        if (programs.current !== program) {\n          gl.useProgram(program);\n          if (!programs.resolution.flagged) {\n            gl.uniform2f(\n              gl.getUniformLocation(program, \"u_resolution\"),\n              programs.resolution.width,\n              programs.resolution.height\n            );\n          }\n          programs.current = program;\n        }\n        if (programs.resolution.flagged) {\n          gl.uniform2f(\n            gl.getUniformLocation(program, \"u_resolution\"),\n            programs.resolution.width,\n            programs.resolution.height\n          );\n        }\n        gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture);\n        gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix);\n        gl.uniform1f(program.size, size * programs.resolution.ratio);\n        gl.drawArrays(gl.POINTS, 0, length);\n        return this.flagReset();\n      }\n    },\n    text: {\n      updateCanvas: function(elem) {\n        const canvas3 = this.canvas;\n        const ctx = this.ctx;\n        const scale = elem._renderer.scale;\n        const stroke = elem._stroke;\n        const linewidth = elem._linewidth * scale;\n        const fill = elem._fill;\n        const opacity = elem._renderer.opacity || elem._opacity;\n        const dashes = elem.dashes;\n        const decoration = elem._decoration;\n        canvas3.width = Math.max(Math.ceil(elem._renderer.rect.width * scale.x), 1);\n        canvas3.height = Math.max(Math.ceil(elem._renderer.rect.height * scale.y), 1);\n        const centroid = elem._renderer.rect.centroid;\n        const cx = centroid.x;\n        const cy = centroid.y;\n        let a, b, c, d, e, sx, sy, x1, y1, x2, y2;\n        const isOffset = fill._renderer && fill._renderer.offset && stroke._renderer && stroke._renderer.offset;\n        ctx.clearRect(0, 0, canvas3.width, canvas3.height);\n        if (!isOffset) {\n          ctx.font = [elem._style, elem._weight, elem._size + \"px/\" + elem._leading + \"px\", elem._family].join(\" \");\n        }\n        ctx.textAlign = \"center\";\n        ctx.textBaseline = \"middle\";\n        if (fill) {\n          if (typeof fill === \"string\") {\n            ctx.fillStyle = fill;\n          } else {\n            webgl[fill._renderer.type].render.call(fill, ctx, elem);\n            ctx.fillStyle = fill._renderer.effect;\n          }\n        }\n        if (stroke) {\n          if (typeof stroke === \"string\") {\n            ctx.strokeStyle = stroke;\n          } else {\n            webgl[stroke._renderer.type].render.call(stroke, ctx, elem);\n            ctx.strokeStyle = stroke._renderer.effect;\n          }\n          if (linewidth) {\n            ctx.lineWidth = linewidth;\n          }\n        }\n        if (typeof opacity === \"number\") {\n          ctx.globalAlpha = opacity;\n        }\n        if (dashes && dashes.length > 0) {\n          ctx.lineDashOffset = dashes.offset || 0;\n          ctx.setLineDash(dashes);\n        }\n        ctx.save();\n        ctx.scale(scale.x, scale.y);\n        ctx.translate(cx, cy);\n        if (!webgl.isHidden.test(fill)) {\n          if (fill._renderer && fill._renderer.offset) {\n            sx = fill._renderer.scale.x;\n            sy = fill._renderer.scale.y;\n            ctx.save();\n            ctx.translate(\n              -fill._renderer.offset.x,\n              -fill._renderer.offset.y\n            );\n            ctx.scale(sx, sy);\n            a = elem._size / fill._renderer.scale.y;\n            b = elem._leading / fill._renderer.scale.y;\n            ctx.font = [\n              elem._style,\n              elem._weight,\n              a + \"px/\",\n              b + \"px\",\n              elem._family\n            ].join(\" \");\n            c = fill._renderer.offset.x / fill._renderer.scale.x;\n            d = fill._renderer.offset.y / fill._renderer.scale.y;\n            ctx.fillText(elem.value, c, d);\n            ctx.restore();\n          } else {\n            ctx.fillText(elem.value, 0, 0);\n          }\n        }\n        if (!webgl.isHidden.test(stroke)) {\n          if (stroke._renderer && stroke._renderer.offset) {\n            sx = stroke._renderer.scale.x;\n            sy = stroke._renderer.scale.y;\n            ctx.save();\n            ctx.translate(\n              -stroke._renderer.offset.x,\n              -stroke._renderer.offset.y\n            );\n            ctx.scale(sx, sy);\n            a = elem._size / stroke._renderer.scale.y;\n            b = elem._leading / stroke._renderer.scale.y;\n            ctx.font = [\n              elem._style,\n              elem._weight,\n              a + \"px/\",\n              b + \"px\",\n              elem._family\n            ].join(\" \");\n            c = stroke._renderer.offset.x / stroke._renderer.scale.x;\n            d = stroke._renderer.offset.y / stroke._renderer.scale.y;\n            e = linewidth / stroke._renderer.scale.x;\n            ctx.lineWidth = e;\n            ctx.strokeText(elem.value, c, d);\n            ctx.restore();\n          } else {\n            ctx.strokeText(elem.value, 0, 0);\n          }\n        }\n        if (/(underline|strikethrough)/i.test(decoration)) {\n          const metrics = ctx.measureText(elem.value);\n          switch (decoration) {\n            case \"underline\":\n              y1 = metrics.actualBoundingBoxAscent;\n              y2 = metrics.actualBoundingBoxAscent;\n              break;\n            case \"strikethrough\":\n              y1 = 0;\n              y2 = 0;\n              break;\n          }\n          x1 = -metrics.width / 2;\n          x2 = metrics.width / 2;\n          ctx.lineWidth = Math.max(Math.floor(elem._size / 15), 1);\n          ctx.strokeStyle = ctx.fillStyle;\n          ctx.beginPath();\n          ctx.moveTo(x1, y1);\n          ctx.lineTo(x2, y2);\n          ctx.stroke();\n        }\n        ctx.restore();\n      },\n      getBoundingClientRect: function(elem, rect) {\n        const ctx = webgl.ctx;\n        ctx.font = [elem._style, elem._weight, elem._size + \"px/\" + elem._leading + \"px\", elem._family].join(\" \");\n        ctx.textAlign = \"center\";\n        ctx.textBaseline = elem._baseline;\n        let width = ctx.measureText(elem._value).width * 1.25;\n        let height = Math.max(elem._size, elem._leading) * 1.25;\n        if (this._linewidth && !webgl.isHidden.test(this._stroke)) {\n          width += this._linewidth * 2;\n          height += this._linewidth * 2;\n        }\n        const w = width / 2;\n        const h = height / 2;\n        switch (webgl.alignments[elem._alignment] || elem._alignment) {\n          case webgl.alignments.left:\n            rect.left = 0;\n            rect.right = width;\n            break;\n          case webgl.alignments.right:\n            rect.left = -width;\n            rect.right = 0;\n            break;\n          default:\n            rect.left = -w;\n            rect.right = w;\n        }\n        switch (elem._baseline) {\n          case \"bottom\":\n            rect.top = -height;\n            rect.bottom = 0;\n            break;\n          case \"top\":\n            rect.top = 0;\n            rect.bottom = height;\n            break;\n          default:\n            rect.top = -h;\n            rect.bottom = h;\n        }\n        rect.width = width;\n        rect.height = height;\n        if (!rect.centroid) {\n          rect.centroid = {};\n        }\n        rect.centroid.x = w;\n        rect.centroid.y = h;\n      },\n      render: function(gl, programs, forcedParent) {\n        if (!this._visible || !this._opacity) {\n          return this;\n        }\n        this._update();\n        const parent = forcedParent || this.parent;\n        const program = programs[this._renderer.type];\n        const flagParentMatrix = parent._matrix.manual || parent._flagMatrix;\n        const flagMatrix = this._matrix.manual || this._flagMatrix;\n        const parentChanged = this._renderer.parent !== parent;\n        const flagTexture = this._flagVertices || this._flagFill || this._fill instanceof LinearGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagEndPoints) || this._fill instanceof RadialGradient && (this._fill._flagSpread || this._fill._flagStops || this._fill._flagRadius || this._fill._flagCenter || this._fill._flagFocal) || this._fill instanceof Texture && (this._fill._flagLoaded && this._fill.loaded || this._fill._flagImage || this._fill._flagVideo || this._fill._flagRepeat || this._fill._flagOffset || this._fill._flagScale) || this._stroke instanceof LinearGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagEndPoints) || this._stroke instanceof RadialGradient && (this._stroke._flagSpread || this._stroke._flagStops || this._stroke._flagRadius || this._stroke._flagCenter || this._stroke._flagFocal) || this._stroke instanceof Texture && (this._stroke._flagLoaded && this._stroke.loaded || this._stroke._flagImage || this._stroke._flagVideo || this._stroke._flagRepeat || this._stroke._flagOffset || this._fill._flagScale) || this._flagStroke || this._flagLinewidth || this._flagOpacity || parent._flagOpacity || this._flagVisible || this._flagScale || this._flagValue || this._flagFamily || this._flagSize || this._flagLeading || this._flagAlignment || this._flagBaseline || this._flagStyle || this._flagWeight || this._flagDecoration || this.dashes && this.dashes.length > 0 || !this._renderer.texture;\n        if (flagParentMatrix || flagMatrix || parentChanged) {\n          if (!this._renderer.matrix) {\n            this._renderer.matrix = new NumArray(9);\n          }\n          this._matrix.toTransformArray(true, transformation);\n          multiplyMatrix(transformation, parent._renderer.matrix, this._renderer.matrix);\n          if (!(this._renderer.scale instanceof Vector)) {\n            this._renderer.scale = new Vector();\n          }\n          if (this._scale instanceof Vector) {\n            this._renderer.scale.x = this._scale.x * parent._renderer.scale.x;\n            this._renderer.scale.y = this._scale.y * parent._renderer.scale.y;\n          } else {\n            this._renderer.scale.x = this._scale * parent._renderer.scale.x;\n            this._renderer.scale.y = this._scale * parent._renderer.scale.y;\n          }\n          if (parentChanged) {\n            this._renderer.parent = parent;\n          }\n        }\n        if (this._mask) {\n          gl.clear(gl.STENCIL_BUFFER_BIT);\n          gl.enable(gl.STENCIL_TEST);\n          gl.stencilFunc(gl.ALWAYS, 1, 0);\n          gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);\n          gl.colorMask(false, false, false, false);\n          webgl[this._mask._renderer.type].render.call(this._mask, gl, programs, this);\n          gl.stencilFunc(gl.EQUAL, 1, 255);\n          gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);\n          gl.colorMask(true, true, true, true);\n        }\n        if (flagTexture) {\n          if (!this._renderer.rect) {\n            this._renderer.rect = {};\n          }\n          this._renderer.opacity = this._opacity * parent._renderer.opacity;\n          webgl.text.getBoundingClientRect(this, this._renderer.rect);\n          webgl.updateTexture.call(webgl, gl, this);\n        } else {\n          if (this._fill && this._fill._update) {\n            this._fill._update();\n          }\n          if (this._stroke && this._stroke._update) {\n            this._stroke._update();\n          }\n        }\n        if (this._clip && !forcedParent || !this._renderer.texture) {\n          return this;\n        }\n        if (programs.current !== program) {\n          gl.useProgram(program);\n          gl.bindBuffer(gl.ARRAY_BUFFER, programs.buffers.position);\n          gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n          gl.enableVertexAttribArray(program.position);\n          gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n          if (!programs.resolution.flagged) {\n            gl.uniform2f(\n              gl.getUniformLocation(program, \"u_resolution\"),\n              programs.resolution.width,\n              programs.resolution.height\n            );\n          }\n          programs.current = program;\n        }\n        if (programs.resolution.flagged) {\n          gl.uniform2f(\n            gl.getUniformLocation(program, \"u_resolution\"),\n            programs.resolution.width,\n            programs.resolution.height\n          );\n        }\n        gl.bindTexture(gl.TEXTURE_2D, this._renderer.texture);\n        const rect = this._renderer.rect;\n        gl.uniformMatrix3fv(program.matrix, false, this._renderer.matrix);\n        gl.uniform4f(program.rect, rect.left, rect.top, rect.right, rect.bottom);\n        gl.drawArrays(gl.TRIANGLES, 0, 6);\n        if (this._mask) {\n          gl.disable(gl.STENCIL_TEST);\n        }\n        return this.flagReset();\n      }\n    },\n    \"linear-gradient\": {\n      render: function(ctx, parent) {\n        if (!ctx.canvas.getContext(\"2d\") || !parent) {\n          return;\n        }\n        this._update();\n        if (!this._renderer.effect || this._flagEndPoints || this._flagStops || this._flagUnits) {\n          let rect;\n          let lx = this.left._x;\n          let ly = this.left._y;\n          let rx = this.right._x;\n          let ry = this.right._y;\n          if (/objectBoundingBox/i.test(this._units)) {\n            rect = parent.getBoundingClientRect(true);\n            lx = (lx - 0.5) * rect.width;\n            ly = (ly - 0.5) * rect.height;\n            rx = (rx - 0.5) * rect.width;\n            ry = (ry - 0.5) * rect.height;\n          }\n          this._renderer.effect = ctx.createLinearGradient(lx, ly, rx, ry);\n          for (let i = 0; i < this.stops.length; i++) {\n            const stop = this.stops[i];\n            this._renderer.effect.addColorStop(stop._offset, stop._color);\n          }\n        }\n        return this.flagReset();\n      }\n    },\n    \"radial-gradient\": {\n      render: function(ctx, parent) {\n        if (!ctx.canvas.getContext(\"2d\") || !parent) {\n          return;\n        }\n        this._update();\n        if (!this._renderer.effect || this._flagCenter || this._flagFocal || this._flagRadius || this._flagStops || this._flagUnits) {\n          let rect;\n          let cx = this.center._x;\n          let cy = this.center._y;\n          let fx = this.focal._x;\n          let fy = this.focal._y;\n          let radius = this._radius;\n          if (/objectBoundingBox/i.test(this._units)) {\n            rect = parent.getBoundingClientRect(true);\n            cx = cx * rect.width * 0.5;\n            cy = cy * rect.height * 0.5;\n            fx = fx * rect.width * 0.5;\n            fy = fy * rect.height * 0.5;\n            radius *= Math.min(rect.width, rect.height) * 0.5;\n          }\n          this._renderer.effect = ctx.createRadialGradient(\n            cx,\n            cy,\n            0,\n            fx,\n            fy,\n            radius\n          );\n          for (let i = 0; i < this.stops.length; i++) {\n            const stop = this.stops[i];\n            this._renderer.effect.addColorStop(stop._offset, stop._color);\n          }\n        }\n        return this.flagReset();\n      }\n    },\n    texture: {\n      render: function(ctx, elem) {\n        if (!ctx.canvas.getContext(\"2d\")) {\n          return;\n        }\n        this._update();\n        const image = this.image;\n        if ((this._flagLoaded || this._flagImage || this._flagVideo || this._flagRepeat) && this.loaded) {\n          this._renderer.effect = ctx.createPattern(image, this._repeat);\n        } else if (!this._renderer.effect) {\n          return this.flagReset();\n        }\n        if (this._flagOffset || this._flagLoaded || this._flagScale) {\n          if (!(this._renderer.offset instanceof Vector)) {\n            this._renderer.offset = new Vector();\n          }\n          this._renderer.offset.x = -this._offset.x;\n          this._renderer.offset.y = -this._offset.y;\n          if (image) {\n            this._renderer.offset.x += image.width / 2;\n            this._renderer.offset.y += image.height / 2;\n            if (this._scale instanceof Vector) {\n              this._renderer.offset.x *= this._scale.x;\n              this._renderer.offset.y *= this._scale.y;\n            } else {\n              this._renderer.offset.x *= this._scale;\n              this._renderer.offset.y *= this._scale;\n            }\n          }\n        }\n        if (this._flagScale || this._flagLoaded) {\n          if (!(this._renderer.scale instanceof Vector)) {\n            this._renderer.scale = new Vector();\n          }\n          if (this._scale instanceof Vector) {\n            this._renderer.scale.copy(this._scale);\n          } else {\n            this._renderer.scale.set(this._scale, this._scale);\n          }\n        }\n        return this.flagReset();\n      }\n    },\n    updateTexture: function(gl, elem) {\n      this[elem._renderer.type].updateCanvas.call(webgl, elem);\n      if (this.canvas.width <= 0 || this.canvas.height <= 0) {\n        if (elem._renderer.texture) {\n          gl.deleteTexture(elem._renderer.texture);\n        }\n        delete elem._renderer.texture;\n        return;\n      }\n      if (!elem._renderer.texture) {\n        elem._renderer.texture = gl.createTexture();\n      }\n      gl.bindTexture(gl.TEXTURE_2D, elem._renderer.texture);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.canvas);\n    },\n    program: {\n      create: function(gl, shaders2) {\n        let program, linked, error;\n        program = gl.createProgram();\n        _.each(shaders2, function(s) {\n          gl.attachShader(program, s);\n        });\n        gl.linkProgram(program);\n        linked = gl.getProgramParameter(program, gl.LINK_STATUS);\n        if (!linked) {\n          error = gl.getProgramInfoLog(program);\n          gl.deleteProgram(program);\n          throw new TwoError(\"unable to link program: \" + error);\n        }\n        return program;\n      }\n    },\n    TextureRegistry: new Registry()\n  };\n  webgl.ctx = webgl.canvas.getContext(\"2d\");\n  var Renderer3 = class extends Events {\n    constructor(params) {\n      super();\n      let gl, program, vs, fs;\n      this.domElement = params.domElement || document.createElement(\"canvas\");\n      if (typeof params.offscreenElement !== \"undefined\") {\n        webgl.canvas = params.offscreenElement;\n        webgl.ctx = webgl.canvas.getContext(\"2d\");\n      }\n      this.scene = new Group();\n      this.scene.parent = this;\n      this._renderer = {\n        type: \"renderer\",\n        matrix: new NumArray(identity),\n        scale: 1,\n        opacity: 1\n      };\n      this._flagMatrix = true;\n      params = _.defaults(params || {}, {\n        antialias: false,\n        alpha: true,\n        premultipliedAlpha: true,\n        stencil: true,\n        preserveDrawingBuffer: true,\n        overdraw: false\n      });\n      this.overdraw = params.overdraw;\n      gl = this.ctx = this.domElement.getContext(\"webgl\", params) || this.domElement.getContext(\"experimental-webgl\", params);\n      if (!this.ctx) {\n        throw new TwoError(\n          \"unable to create a webgl context. Try using another renderer.\"\n        );\n      }\n      vs = shaders.create(gl, shaders.path.vertex, shaders.types.vertex);\n      fs = shaders.create(gl, shaders.path.fragment, shaders.types.fragment);\n      this.programs = {\n        current: null,\n        buffers: {\n          position: gl.createBuffer()\n        },\n        resolution: {\n          width: 0,\n          height: 0,\n          ratio: 1,\n          flagged: false\n        }\n      };\n      program = this.programs.path = webgl.program.create(gl, [vs, fs]);\n      this.programs.text = this.programs.path;\n      program.position = gl.getAttribLocation(program, \"a_position\");\n      program.matrix = gl.getUniformLocation(program, \"u_matrix\");\n      program.rect = gl.getUniformLocation(program, \"u_rect\");\n      const positionBuffer = gl.createBuffer();\n      gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);\n      gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);\n      gl.enableVertexAttribArray(program.position);\n      gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);\n      vs = shaders.create(gl, shaders.points.vertex, shaders.types.vertex);\n      fs = shaders.create(gl, shaders.points.fragment, shaders.types.fragment);\n      program = this.programs.points = webgl.program.create(gl, [vs, fs]);\n      program.position = gl.getAttribLocation(program, \"a_position\");\n      program.matrix = gl.getUniformLocation(program, \"u_matrix\");\n      program.size = gl.getUniformLocation(program, \"u_size\");\n      gl.enable(gl.BLEND);\n      gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);\n      gl.blendEquation(gl.FUNC_ADD);\n      gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n    }\n    setSize(width, height, ratio) {\n      let w, h;\n      const ctx = this.ctx;\n      this.width = width;\n      this.height = height;\n      this.ratio = typeof ratio === \"undefined\" ? getRatio(ctx) : ratio;\n      this.domElement.width = width * this.ratio;\n      this.domElement.height = height * this.ratio;\n      if (_.isObject(this.domElement.style)) {\n        _.extend(this.domElement.style, {\n          width: width + \"px\",\n          height: height + \"px\"\n        });\n      }\n      this._renderer.matrix[0] = this._renderer.matrix[4] = this._renderer.scale = this.ratio;\n      this._flagMatrix = true;\n      w = width * this.ratio;\n      h = height * this.ratio;\n      ctx.viewport(0, 0, w, h);\n      this.programs.resolution.width = w;\n      this.programs.resolution.height = h;\n      this.programs.resolution.ratio = this.ratio;\n      this.programs.resolution.flagged = true;\n      return this.trigger(Events.Types.resize, width, height, ratio);\n    }\n    render() {\n      const gl = this.ctx;\n      if (!this.overdraw) {\n        gl.clear(gl.COLOR_BUFFER_BIT);\n      }\n      webgl.group.render.call(this.scene, gl, this.programs);\n      this._flagMatrix = false;\n      this.programs.resolution.flagged = true;\n      return this;\n    }\n  };\n  __publicField(Renderer3, \"Utils\", webgl);\n\n  // src/two.js\n  var Utils = _.extend({\n    Error: TwoError,\n    getRatio,\n    read,\n    xhr\n  }, _, CanvasShim, curves_exports, math_exports);\n  var _Two = class {\n    _events = new Events();\n    get _bound() {\n      return this._events._bound;\n    }\n    set _bound(v) {\n      this._events._bound = v;\n    }\n    addEventListener() {\n      return this._events.addEventListener.apply(this, arguments);\n    }\n    on() {\n      return this._events.addEventListener.apply(this, arguments);\n    }\n    bind() {\n      return this._events.addEventListener.apply(this, arguments);\n    }\n    removeEventListener() {\n      return this._events.removeEventListener.apply(this, arguments);\n    }\n    off() {\n      return this._events.removeEventListener.apply(this, arguments);\n    }\n    unbind() {\n      return this._events.removeEventListener.apply(this, arguments);\n    }\n    dispatchEvent() {\n      return this._events.dispatchEvent.apply(this, arguments);\n    }\n    trigger() {\n      return this._events.dispatchEvent.apply(this, arguments);\n    }\n    listen() {\n      return this._events.listen.apply(this, arguments);\n    }\n    ignore() {\n      return this._events.ignore.apply(this, arguments);\n    }\n    type = \"\";\n    renderer = null;\n    scene = null;\n    width = 0;\n    height = 0;\n    frameCount = 0;\n    timeDelta = 0;\n    playing = false;\n    constructor(options) {\n      const params = _.defaults(options || {}, {\n        fullscreen: false,\n        fitted: false,\n        width: 640,\n        height: 480,\n        type: _Two.Types.svg,\n        autostart: false\n      });\n      _.each(params, function(v, k) {\n        if (/fullscreen/i.test(k) || /autostart/i.test(k)) {\n          return;\n        }\n        this[k] = v;\n      }, this);\n      if (_.isElement(params.domElement)) {\n        const tagName = params.domElement.tagName.toLowerCase();\n        if (!/^(CanvasRenderer-canvas|WebGLRenderer-canvas|SVGRenderer-svg)$/.test(this.type + \"-\" + tagName)) {\n          this.type = _Two.Types[tagName];\n        }\n      }\n      this.renderer = new _Two[this.type](this);\n      this.setPlaying(params.autostart);\n      this.frameCount = 0;\n      if (params.fullscreen) {\n        this.fit = fitToWindow.bind(this);\n        this.fit.domElement = window;\n        this.fit.attached = true;\n        _.extend(document.body.style, {\n          overflow: \"hidden\",\n          margin: 0,\n          padding: 0,\n          top: 0,\n          left: 0,\n          right: 0,\n          bottom: 0,\n          position: \"fixed\"\n        });\n        _.extend(this.renderer.domElement.style, {\n          display: \"block\",\n          top: 0,\n          left: 0,\n          right: 0,\n          bottom: 0,\n          position: \"fixed\"\n        });\n        dom.bind(this.fit.domElement, \"resize\", this.fit);\n        this.fit();\n      } else if (params.fitted) {\n        this.fit = fitToParent.bind(this);\n        _.extend(this.renderer.domElement.style, {\n          display: \"block\"\n        });\n      } else if (!_.isElement(params.domElement)) {\n        this.renderer.setSize(params.width, params.height, this.ratio);\n        this.width = params.width;\n        this.height = params.height;\n      }\n      this.renderer.bind(Events.Types.resize, updateDimensions.bind(this));\n      this.scene = this.renderer.scene;\n      _Two.Instances.push(this);\n      if (params.autostart) {\n        raf.init();\n      }\n    }\n    appendTo(elem) {\n      elem.appendChild(this.renderer.domElement);\n      if (this.fit) {\n        if (this.fit.domElement !== window) {\n          this.fit.domElement = elem;\n          this.fit.attached = false;\n        }\n        this.update();\n      }\n      return this;\n    }\n    play() {\n      this.playing = true;\n      raf.init();\n      return this.trigger(Events.Types.play);\n    }\n    pause() {\n      this.playing = false;\n      return this.trigger(Events.Types.pause);\n    }\n    setPlaying(p) {\n      this.playing = p;\n    }\n    release(obj) {\n      let i, v, child;\n      if (!_.isObject(obj)) {\n        return this.release(this.scene);\n      }\n      if (typeof obj.unbind === \"function\") {\n        obj.unbind();\n      }\n      if (obj.vertices) {\n        if (typeof obj.vertices.unbind === \"function\") {\n          obj.vertices.unbind();\n        }\n        for (i = 0; i < obj.vertices.length; i++) {\n          v = obj.vertices[i];\n          if (typeof v.unbind === \"function\") {\n            v.unbind();\n          }\n          if (v.controls) {\n            if (v.controls.left && typeof v.controls.left.unbind === \"function\") {\n              v.controls.left.unbind();\n            }\n            if (v.controls.right && typeof v.controls.right.unbind === \"function\") {\n              v.controls.right.unbind();\n            }\n          }\n        }\n      }\n      if (obj.children) {\n        for (i = 0; i < obj.children.length; i++) {\n          child = obj.children[i];\n          this.release(child);\n        }\n        if (typeof obj.children.unbind === \"function\") {\n          obj.children.unbind();\n        }\n      }\n      return obj;\n    }\n    update() {\n      const animated = !!this._lastFrame;\n      const now = _.performance.now();\n      if (animated) {\n        this.timeDelta = parseFloat((now - this._lastFrame).toFixed(3));\n      }\n      this._lastFrame = now;\n      if (this.fit && this.fit.domElement && !this.fit.attached) {\n        dom.bind(this.fit.domElement, \"resize\", this.fit);\n        this.fit.attached = true;\n        this.fit();\n      }\n      const width = this.width;\n      const height = this.height;\n      const renderer = this.renderer;\n      if (width !== renderer.width || height !== renderer.height) {\n        renderer.setSize(width, height, this.ratio);\n      }\n      this.trigger(Events.Types.update, this.frameCount, this.timeDelta);\n      return this.render();\n    }\n    render() {\n      this.renderer.render();\n      return this.trigger(Events.Types.render, this.frameCount++);\n    }\n    add(objects) {\n      if (!(objects instanceof Array)) {\n        objects = Array.prototype.slice.call(arguments);\n      }\n      this.scene.add(objects);\n      return this;\n    }\n    remove(objects) {\n      if (!(objects instanceof Array)) {\n        objects = Array.prototype.slice.call(arguments);\n      }\n      this.scene.remove(objects);\n      return this;\n    }\n    clear() {\n      this.scene.remove(this.scene.children);\n      return this;\n    }\n    makeLine(x1, y1, x2, y2) {\n      const line = new Line(x1, y1, x2, y2);\n      this.scene.add(line);\n      return line;\n    }\n    makeArrow(x1, y1, x2, y2, size) {\n      const headlen = typeof size === \"number\" ? size : 10;\n      const angle = Math.atan2(y2 - y1, x2 - x1);\n      const vertices = [\n        new Anchor(x1, y1, void 0, void 0, void 0, void 0, Commands.move),\n        new Anchor(x2, y2, void 0, void 0, void 0, void 0, Commands.line),\n        new Anchor(\n          x2 - headlen * Math.cos(angle - Math.PI / 4),\n          y2 - headlen * Math.sin(angle - Math.PI / 4),\n          void 0,\n          void 0,\n          void 0,\n          void 0,\n          Commands.line\n        ),\n        new Anchor(x2, y2, void 0, void 0, void 0, void 0, Commands.move),\n        new Anchor(\n          x2 - headlen * Math.cos(angle + Math.PI / 4),\n          y2 - headlen * Math.sin(angle + Math.PI / 4),\n          void 0,\n          void 0,\n          void 0,\n          void 0,\n          Commands.line\n        )\n      ];\n      const path = new Path(vertices, false, false, true);\n      path.noFill();\n      path.cap = \"round\";\n      path.join = \"round\";\n      this.scene.add(path);\n      return path;\n    }\n    makeRectangle(x, y, width, height) {\n      const rect = new Rectangle(x, y, width, height);\n      this.scene.add(rect);\n      return rect;\n    }\n    makeRoundedRectangle(x, y, width, height, sides) {\n      const rect = new RoundedRectangle(x, y, width, height, sides);\n      this.scene.add(rect);\n      return rect;\n    }\n    makeCircle(x, y, radius, resolution) {\n      const circle = new Circle(x, y, radius, resolution);\n      this.scene.add(circle);\n      return circle;\n    }\n    makeEllipse(x, y, rx, ry, resolution) {\n      const ellipse = new Ellipse(x, y, rx, ry, resolution);\n      this.scene.add(ellipse);\n      return ellipse;\n    }\n    makeStar(x, y, outerRadius, innerRadius, sides) {\n      const star = new Star(x, y, outerRadius, innerRadius, sides);\n      this.scene.add(star);\n      return star;\n    }\n    makeCurve(points) {\n      const l = arguments.length;\n      if (!Array.isArray(points)) {\n        points = [];\n        for (let i = 0; i < l; i += 2) {\n          const x = arguments[i];\n          if (typeof x !== \"number\") {\n            break;\n          }\n          const y = arguments[i + 1];\n          points.push(new Anchor(x, y));\n        }\n      }\n      const last = arguments[l - 1];\n      const curve = new Path(points, !(typeof last === \"boolean\" ? last : void 0), true);\n      const rect = curve.getBoundingClientRect();\n      curve.center().translation.set(rect.left + rect.width / 2, rect.top + rect.height / 2);\n      this.scene.add(curve);\n      return curve;\n    }\n    makePolygon(x, y, radius, sides) {\n      const poly = new Polygon(x, y, radius, sides);\n      this.scene.add(poly);\n      return poly;\n    }\n    makeArcSegment(x, y, innerRadius, outerRadius, startAngle, endAngle, resolution) {\n      const arcSegment = new ArcSegment(\n        x,\n        y,\n        innerRadius,\n        outerRadius,\n        startAngle,\n        endAngle,\n        resolution\n      );\n      this.scene.add(arcSegment);\n      return arcSegment;\n    }\n    makePoints(p) {\n      const l = arguments.length;\n      let vertices = p;\n      if (!Array.isArray(p)) {\n        vertices = [];\n        for (let i = 0; i < l; i += 2) {\n          const x = arguments[i];\n          if (typeof x !== \"number\") {\n            break;\n          }\n          const y = arguments[i + 1];\n          vertices.push(new Vector(x, y));\n        }\n      }\n      const points = new Points(vertices);\n      this.scene.add(points);\n      return points;\n    }\n    makePath(p) {\n      const l = arguments.length;\n      let points = p;\n      if (!Array.isArray(p)) {\n        points = [];\n        for (let i = 0; i < l; i += 2) {\n          const x = arguments[i];\n          if (typeof x !== \"number\") {\n            break;\n          }\n          const y = arguments[i + 1];\n          points.push(new Anchor(x, y));\n        }\n      }\n      const last = arguments[l - 1];\n      const path = new Path(points, !(typeof last === \"boolean\" ? last : void 0));\n      const rect = path.getBoundingClientRect();\n      if (typeof rect.top === \"number\" && typeof rect.left === \"number\" && typeof rect.right === \"number\" && typeof rect.bottom === \"number\") {\n        path.center().translation.set(rect.left + rect.width / 2, rect.top + rect.height / 2);\n      }\n      this.scene.add(path);\n      return path;\n    }\n    makeText(message, x, y, styles) {\n      const text = new Text(message, x, y, styles);\n      this.add(text);\n      return text;\n    }\n    makeLinearGradient(x1, y1, x2, y2) {\n      const stops = Array.prototype.slice.call(arguments, 4);\n      const gradient = new LinearGradient(x1, y1, x2, y2, stops);\n      this.add(gradient);\n      return gradient;\n    }\n    makeRadialGradient(x1, y1, radius) {\n      const stops = Array.prototype.slice.call(arguments, 3);\n      const gradient = new RadialGradient(x1, y1, radius, stops);\n      this.add(gradient);\n      return gradient;\n    }\n    makeSprite(pathOrTexture, x, y, columns, rows, frameRate, autostart) {\n      const sprite = new Sprite(pathOrTexture, x, y, columns, rows, frameRate);\n      if (autostart) {\n        sprite.play();\n      }\n      this.add(sprite);\n      return sprite;\n    }\n    makeImageSequence(pathsOrTextures, x, y, frameRate, autostart) {\n      const imageSequence = new ImageSequence(pathsOrTextures, x, y, frameRate);\n      if (autostart) {\n        imageSequence.play();\n      }\n      this.add(imageSequence);\n      return imageSequence;\n    }\n    makeTexture(pathOrSource, callback) {\n      const texture = new Texture(pathOrSource, callback);\n      return texture;\n    }\n    makeGroup(objects) {\n      if (!(objects instanceof Array)) {\n        objects = Array.prototype.slice.call(arguments);\n      }\n      const group = new Group();\n      this.scene.add(group);\n      group.add(objects);\n      return group;\n    }\n    interpret(svg2, shallow, add) {\n      const tag = svg2.tagName.toLowerCase();\n      add = typeof add !== \"undefined\" ? add : true;\n      if (!(tag in read)) {\n        return null;\n      }\n      const node = read[tag].call(this, svg2);\n      if (add) {\n        this.add(shallow && node instanceof Group ? node.children : node);\n      } else if (node.parent) {\n        node.remove();\n      }\n      return node;\n    }\n    load(pathOrSVGContent, callback) {\n      const group = new Group();\n      let elem, i, child;\n      const attach = function(data) {\n        dom.temp.innerHTML = data;\n        for (i = 0; i < dom.temp.children.length; i++) {\n          elem = dom.temp.children[i];\n          child = this.interpret(elem, false, false);\n          if (child !== null) {\n            group.add(child);\n          }\n        }\n        if (typeof callback === \"function\") {\n          const svg2 = dom.temp.children.length <= 1 ? dom.temp.children[0] : dom.temp.children;\n          callback(group, svg2);\n        }\n      }.bind(this);\n      if (/\\.svg$/i.test(pathOrSVGContent)) {\n        xhr(pathOrSVGContent, attach);\n        return group;\n      }\n      attach(pathOrSVGContent);\n      return group;\n    }\n  };\n  var Two = _Two;\n  __publicField(Two, \"nextFrameID\", Constants.nextFrameID);\n  __publicField(Two, \"Types\", Constants.Types);\n  __publicField(Two, \"Version\", Constants.Version);\n  __publicField(Two, \"PublishDate\", Constants.PublishDate);\n  __publicField(Two, \"Identifier\", Constants.Identifier);\n  __publicField(Two, \"Resolution\", Constants.Resolution);\n  __publicField(Two, \"AutoCalculateImportedMatrices\", Constants.AutoCalculateImportedMatrices);\n  __publicField(Two, \"Instances\", Constants.Instances);\n  __publicField(Two, \"uniqueId\", Constants.uniqueId);\n  __publicField(Two, \"Anchor\", Anchor);\n  __publicField(Two, \"Collection\", Collection);\n  __publicField(Two, \"Events\", Events);\n  __publicField(Two, \"Group\", Group);\n  __publicField(Two, \"Matrix\", Matrix2);\n  __publicField(Two, \"Path\", Path);\n  __publicField(Two, \"Registry\", Registry);\n  __publicField(Two, \"Shape\", Shape);\n  __publicField(Two, \"Text\", Text);\n  __publicField(Two, \"Vector\", Vector);\n  __publicField(Two, \"Gradient\", Gradient);\n  __publicField(Two, \"ImageSequence\", ImageSequence);\n  __publicField(Two, \"LinearGradient\", LinearGradient);\n  __publicField(Two, \"RadialGradient\", RadialGradient);\n  __publicField(Two, \"Sprite\", Sprite);\n  __publicField(Two, \"Stop\", Stop);\n  __publicField(Two, \"Texture\", Texture);\n  __publicField(Two, \"ArcSegment\", ArcSegment);\n  __publicField(Two, \"Circle\", Circle);\n  __publicField(Two, \"Ellipse\", Ellipse);\n  __publicField(Two, \"Line\", Line);\n  __publicField(Two, \"Points\", Points);\n  __publicField(Two, \"Polygon\", Polygon);\n  __publicField(Two, \"Rectangle\", Rectangle);\n  __publicField(Two, \"RoundedRectangle\", RoundedRectangle);\n  __publicField(Two, \"Star\", Star);\n  __publicField(Two, \"CanvasRenderer\", Renderer);\n  __publicField(Two, \"SVGRenderer\", Renderer2);\n  __publicField(Two, \"WebGLRenderer\", Renderer3);\n  __publicField(Two, \"Commands\", Commands);\n  __publicField(Two, \"Utils\", Utils);\n  function fitToWindow() {\n    const wr = document.body.getBoundingClientRect();\n    const width = this.width = wr.width;\n    const height = this.height = wr.height;\n    this.renderer.setSize(width, height, this.ratio);\n  }\n  function fitToParent() {\n    const parent = this.renderer.domElement.parentElement;\n    if (!parent) {\n      console.warn(\"Two.js: Attempting to fit to parent, but no parent found.\");\n      return;\n    }\n    const wr = parent.getBoundingClientRect();\n    const width = this.width = wr.width;\n    const height = this.height = wr.height;\n    this.renderer.setSize(width, height, this.ratio);\n  }\n  function updateDimensions(width, height) {\n    this.width = width;\n    this.height = height;\n    this.trigger(Events.Types.resize, width, height);\n  }\n  var raf = dom.getRequestAnimationFrame();\n  function loop() {\n    for (let i = 0; i < Two.Instances.length; i++) {\n      const t = Two.Instances[i];\n      if (t.playing) {\n        t.update();\n      }\n    }\n    Two.nextFrameID = raf(loop);\n  }\n  raf.init = function() {\n    loop();\n    raf.init = function() {\n    };\n  };\n  return __toCommonJS(two_exports);\n})().default;\n\n(function(){if(typeof exports==='object'&&typeof module!=='undefined'){module.exports=Two}})()"
  },
  {
    "path": "models/layers/gilbert/gilbert2d.py",
    "content": "#!/usr/bin/env python3\n# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2018 Jakub Červený\n\n\ndef gilbert2d(width, height):\n    \"\"\"\n    Generalized Hilbert ('gilbert') space-filling curve for arbitrary-sized\n    2D rectangular grids. Generates discrete 2D coordinates to fill a rectangle\n    of size (width x height).\n    \"\"\"\n\n    if width >= height:\n        yield from generate2d(0, 0, width, 0, 0, height)\n    else:\n        yield from generate2d(0, 0, 0, height, width, 0)\n\ndef gilbert2d_widthBigger(width, height):\n    \"\"\"\n    Generalized Hilbert ('gilbert') space-filling curve for arbitrary-sized\n    2D rectangular grids. Generates discrete 2D coordinates to fill a rectangle\n    of size (width x height).\n    \"\"\"\n\n    yield from generate2d(0, 0, width, 0, 0, height)\n\ndef sgn(x):\n    return -1 if x < 0 else (1 if x > 0 else 0)\n\n\ndef generate2d(x, y, ax, ay, bx, by):\n\n    w = abs(ax + ay)\n    h = abs(bx + by)\n\n    (dax, day) = (sgn(ax), sgn(ay)) # unit major direction\n    (dbx, dby) = (sgn(bx), sgn(by)) # unit orthogonal direction\n\n    if h == 1:\n        # trivial row fill\n        for i in range(0, w):\n            yield(x, y)\n            (x, y) = (x + dax, y + day)\n        return\n\n    if w == 1:\n        # trivial column fill\n        for i in range(0, h):\n            yield(x, y)\n            (x, y) = (x + dbx, y + dby)\n        return\n\n    (ax2, ay2) = (ax//2, ay//2)\n    (bx2, by2) = (bx//2, by//2)\n\n    w2 = abs(ax2 + ay2)\n    h2 = abs(bx2 + by2)\n\n    if 2*w > 3*h:\n        if (w2 % 2) and (w > 2):\n            # prefer even steps\n            (ax2, ay2) = (ax2 + dax, ay2 + day)\n\n        # long case: split in two parts only\n        yield from generate2d(x, y, ax2, ay2, bx, by)\n        yield from generate2d(x+ax2, y+ay2, ax-ax2, ay-ay2, bx, by)\n\n    else:\n        if (h2 % 2) and (h > 2):\n            # prefer even steps\n            (bx2, by2) = (bx2 + dbx, by2 + dby)\n\n        # standard case: one step up, one long horizontal, one step down\n        yield from generate2d(x, y, bx2, by2, ax2, ay2)\n        yield from generate2d(x+bx2, y+by2, ax, ay, bx-bx2, by-by2)\n        yield from generate2d(x+(ax-dax)+(bx2-dbx), y+(ay-day)+(by2-dby),\n                              -bx2, -by2, -(ax-ax2), -(ay-ay2))\n\n\nif __name__ == \"__main__\":\n\n    import argparse\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('width', type=int)\n    parser.add_argument('height', type=int)\n    args = parser.parse_args()\n\n    for x, y in gilbert2d(args.width, args.height):\n        print(x, y)\n"
  },
  {
    "path": "models/layers/gilbert/gilbert3d.py",
    "content": "#!/usr/bin/env python3\n# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2018 Jakub Červený\n\n\ndef gilbert3d(width, height, depth):\n    \"\"\"\n    Generalized Hilbert ('Gilbert') space-filling curve for arbitrary-sized\n    3D rectangular grids. Generates discrete 3D coordinates to fill a cuboid\n    of size (width x height x depth). Even sizes are recommended in 3D.\n    \"\"\"\n\n    if width >= height and width >= depth:\n       yield from generate3d(0, 0, 0,\n                             width, 0, 0,\n                             0, height, 0,\n                             0, 0, depth)\n\n    elif height >= width and height >= depth:\n       yield from generate3d(0, 0, 0,\n                             0, height, 0,\n                             width, 0, 0,\n                             0, 0, depth)\n\n    else: # depth >= width and depth >= height\n       yield from generate3d(0, 0, 0,\n                             0, 0, depth,\n                             width, 0, 0,\n                             0, height, 0)\n\n\ndef sgn(x):\n    return -1 if x < 0 else (1 if x > 0 else 0)\n\n# T H W \ndef generate3d(x, y, z,\n               ax, ay, az,\n               bx, by, bz,\n               cx, cy, cz):\n\n    w = abs(ax + ay + az)\n    h = abs(bx + by + bz)\n    d = abs(cx + cy + cz)\n\n    (dax, day, daz) = (sgn(ax), sgn(ay), sgn(az)) # unit major direction (\"right\")\n    (dbx, dby, dbz) = (sgn(bx), sgn(by), sgn(bz)) # unit ortho direction (\"forward\")\n    (dcx, dcy, dcz) = (sgn(cx), sgn(cy), sgn(cz)) # unit ortho direction (\"up\")\n\n    # trivial row/column fills\n    if h == 1 and d == 1:\n        for i in range(0, w):\n            yield(x, y, z)\n            (x, y, z) = (x + dax, y + day, z + daz)\n        return\n\n    if w == 1 and d == 1:\n        for i in range(0, h):\n            yield(x, y, z)\n            (x, y, z) = (x + dbx, y + dby, z + dbz)\n        return\n\n    if w == 1 and h == 1:\n        for i in range(0, d):\n            yield(x, y, z)\n            (x, y, z) = (x + dcx, y + dcy, z + dcz)\n        return\n\n    (ax2, ay2, az2) = (ax//2, ay//2, az//2)\n    (bx2, by2, bz2) = (bx//2, by//2, bz//2)\n    (cx2, cy2, cz2) = (cx//2, cy//2, cz//2)\n\n    w2 = abs(ax2 + ay2 + az2)\n    h2 = abs(bx2 + by2 + bz2)\n    d2 = abs(cx2 + cy2 + cz2)\n\n    # prefer even steps\n    if (w2 % 2) and (w > 2):\n       (ax2, ay2, az2) = (ax2 + dax, ay2 + day, az2 + daz)\n\n    if (h2 % 2) and (h > 2):\n       (bx2, by2, bz2) = (bx2 + dbx, by2 + dby, bz2 + dbz)\n\n    if (d2 % 2) and (d > 2):\n       (cx2, cy2, cz2) = (cx2 + dcx, cy2 + dcy, cz2 + dcz)\n\n    # wide case, split in w only\n    if (2*w > 3*h) and (2*w > 3*d):\n       yield from generate3d(x, y, z,\n                             ax2, ay2, az2,\n                             bx, by, bz,\n                             cx, cy, cz)\n\n       yield from generate3d(x+ax2, y+ay2, z+az2,\n                             ax-ax2, ay-ay2, az-az2,\n                             bx, by, bz,\n                             cx, cy, cz)\n\n    # do not split in d\n    elif 3*h > 4*d:\n       yield from generate3d(x, y, z,\n                             bx2, by2, bz2,\n                             cx, cy, cz,\n                             ax2, ay2, az2)\n\n       yield from generate3d(x+bx2, y+by2, z+bz2,\n                             ax, ay, az,\n                             bx-bx2, by-by2, bz-bz2,\n                             cx, cy, cz)\n\n       yield from generate3d(x+(ax-dax)+(bx2-dbx),\n                             y+(ay-day)+(by2-dby),\n                             z+(az-daz)+(bz2-dbz),\n                             -bx2, -by2, -bz2,\n                             cx, cy, cz,\n                             -(ax-ax2), -(ay-ay2), -(az-az2))\n\n    # do not split in h\n    elif 3*d > 4*h:\n       yield from generate3d(x, y, z,\n                             cx2, cy2, cz2,\n                             ax2, ay2, az2,\n                             bx, by, bz)\n\n       yield from generate3d(x+cx2, y+cy2, z+cz2,\n                             ax, ay, az,\n                             bx, by, bz,\n                             cx-cx2, cy-cy2, cz-cz2)\n\n       yield from generate3d(x+(ax-dax)+(cx2-dcx),\n                             y+(ay-day)+(cy2-dcy),\n                             z+(az-daz)+(cz2-dcz),\n                             -cx2, -cy2, -cz2,\n                             -(ax-ax2), -(ay-ay2), -(az-az2),\n                             bx, by, bz)\n\n    # regular case, split in all w/h/d\n    else:\n       yield from generate3d(x, y, z,\n                             bx2, by2, bz2,\n                             cx2, cy2, cz2,\n                             ax2, ay2, az2)\n\n       yield from generate3d(x+bx2, y+by2, z+bz2,\n                             cx, cy, cz,\n                             ax2, ay2, az2,\n                             bx-bx2, by-by2, bz-bz2)\n\n       yield from generate3d(x+(bx2-dbx)+(cx-dcx),\n                             y+(by2-dby)+(cy-dcy),\n                             z+(bz2-dbz)+(cz-dcz),\n                             ax, ay, az,\n                             -bx2, -by2, -bz2,\n                             -(cx-cx2), -(cy-cy2), -(cz-cz2))\n\n       yield from generate3d(x+(ax-dax)+bx2+(cx-dcx),\n                             y+(ay-day)+by2+(cy-dcy),\n                             z+(az-daz)+bz2+(cz-dcz),\n                             -cx, -cy, -cz,\n                             -(ax-ax2), -(ay-ay2), -(az-az2),\n                             bx-bx2, by-by2, bz-bz2)\n\n       yield from generate3d(x+(ax-dax)+(bx2-dbx),\n                             y+(ay-day)+(by2-dby),\n                             z+(az-daz)+(bz2-dbz),\n                             -bx2, -by2, -bz2,\n                             cx2, cy2, cz2,\n                             -(ax-ax2), -(ay-ay2), -(az-az2))\n\n\nif __name__ == \"__main__\":\n\n    import argparse\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('width', type=int)\n    parser.add_argument('height', type=int)\n    parser.add_argument('depth', type=int)\n    args = parser.parse_args()\n\n    for x, y, z in gilbert3d(args.width, args.height, args.depth):\n        print(x, y, z)\n"
  },
  {
    "path": "models/layers/gilbert/gilbert_d2xy.py",
    "content": "#!/usr/bin/env python3\n# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2024 abetusk\n\ndef gilbert_d2xy(idx, w, h):\n    \"\"\"\n    Generalized Hilbert ('gilbert') space-filling curve for arbitrary-sized\n    2D rectangular grids. Takes a position along the gilbert curve and returns\n    its 2D (x,y) coordinate.\n    \"\"\"\n\n    if w >= h:\n        return gilbert_d2xy_r(idx,0, 0,0, w,0, 0,h)\n    return gilbert_d2xy_r(idx,0, 0,0, 0,h, w,0)\n\ndef sgn(x):\n    return -1 if x < 0 else (1 if x > 0 else 0)\n\ndef gilbert_d2xy_r(dst_idx, cur_idx, x,y, ax,ay, bx,by):\n\n    w = abs(ax + ay)\n    h = abs(bx + by)\n\n    (dax, day) = (sgn(ax), sgn(ay)) # unit major direction\n    (dbx, dby) = (sgn(bx), sgn(by)) # unit orthogonal direction\n\n    dx = dax + dbx\n    dy = day + dby\n    di = dst_idx - cur_idx\n\n    if h == 1: return (x + dax*di, y + day*di)\n    if w == 1: return (x + dbx*di, y + dby*di)\n\n    (ax2, ay2) = (ax//2, ay//2)\n    (bx2, by2) = (bx//2, by//2)\n\n    w2 = abs(ax2 + ay2)\n    h2 = abs(bx2 + by2)\n\n    if 2*w > 3*h:\n        if (w2 % 2) and (w > 2):\n            # prefer even steps\n            (ax2, ay2) = (ax2 + dax, ay2 + day)\n\n\n        # long case: split in two parts only\n        nxt_idx = cur_idx + abs((ax2 + ay2)*(bx + by))\n        if (cur_idx <= dst_idx) and (dst_idx < nxt_idx):\n            return gilbert_d2xy_r(dst_idx, cur_idx,  x, y, ax2, ay2, bx, by)\n        cur_idx = nxt_idx\n\n        return gilbert_d2xy_r(dst_idx, cur_idx, x+ax2, y+ay2, ax-ax2, ay-ay2, bx, by)\n\n    if (h2 % 2) and (h > 2):\n        # prefer even steps\n        (bx2, by2) = (bx2 + dbx, by2 + dby)\n\n    # standard case: one step up, one long horizontal, one step down\n    nxt_idx = cur_idx + abs((bx2 + by2)*(ax2 + ay2))\n    if (cur_idx <= dst_idx) and (dst_idx < nxt_idx):\n        return gilbert_d2xy_r(dst_idx, cur_idx, x,y, bx2,by2, ax2,ay2)\n    cur_idx = nxt_idx\n\n    nxt_idx = cur_idx + abs((ax + ay)*((bx - bx2) + (by - by2)))\n    if (cur_idx <= dst_idx) and (dst_idx < nxt_idx):\n        return gilbert_d2xy_r(dst_idx, cur_idx, x+bx2, y+by2, ax,ay, bx-bx2,by-by2)\n    cur_idx = nxt_idx\n\n    return gilbert_d2xy_r(dst_idx, cur_idx,\n                          x+(ax-dax)+(bx2-dbx),\n                          y+(ay-day)+(by2-dby),\n                          -bx2, -by2,\n                          -(ax-ax2), -(ay-ay2))\n\nif __name__ == \"__main__\":\n\n    import argparse\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('width', type=int)\n    parser.add_argument('height', type=int)\n    args = parser.parse_args()\n\n    width = args.width\n    height = args.height\n\n    for idx in range(width*height):\n      (x,y) = gilbert_d2xy(idx, width,height)\n      print(x,y)\n\n"
  },
  {
    "path": "models/layers/gilbert/gilbert_d2xyz.py",
    "content": "#!/usr/bin/env python3\n# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2024 abetusk\n\ndef gilbert_d2xyz(idx, width, height, depth):\n    \"\"\"\n    Generalized Hilbert ('Gilbert') space-filling curve for arbitrary-sized\n    3D rectangular grids. Generates discrete 3D coordinates to fill a cuboid\n    of size (width x height x depth). Even sizes are recommended in 3D.\n    \"\"\"\n\n    if width >= height and width >= depth:\n       return gilbert_d2xyz_r(idx, 0,\n                              0, 0, 0,\n                              width, 0, 0,\n                              0, height, 0,\n                              0, 0, depth)\n\n    elif height >= width and height >= depth:\n       return gilbert_d2xyz_r(idx, 0,\n                              0, 0, 0,\n                              0, height, 0,\n                              width, 0, 0,\n                              0, 0, depth)\n\n    else: # depth >= width and depth >= height\n       return gilbert_d2xyz_r(idx, 0,\n                              0, 0, 0,\n                              0, 0, depth,\n                              width, 0, 0,\n                              0, height, 0)\n\ndef sgn(x):\n    return -1 if x < 0 else (1 if x > 0 else 0)\n\ndef gilbert_d2xyz_r(dst_idx, cur_idx,\n                    x, y, z,\n                    ax, ay, az,\n                    bx, by, bz,\n                    cx, cy, cz):\n\n    w = abs(ax + ay + az)\n    h = abs(bx + by + bz)\n    d = abs(cx + cy + cz)\n\n    (dax, day, daz) = (sgn(ax), sgn(ay), sgn(az)) # unit major direction (\"right\")\n    (dbx, dby, dbz) = (sgn(bx), sgn(by), sgn(bz)) # unit ortho direction (\"forward\")\n    (dcx, dcy, dcz) = (sgn(cx), sgn(cy), sgn(cz)) # unit ortho direction (\"up\")\n\n    _dx = dax + dbx + dcx\n    _dy = day + dby + dcy\n    _dz = daz + dbz + dcz\n    _di = dst_idx - cur_idx\n\n    # trivial row/column fills\n    if h == 1 and d == 1:\n        return (x + dax*_di, y + day*_di, z + daz*_di)\n\n    if w == 1 and d == 1:\n        return (x + dbx*_di, y + dby*_di, z + dbz*_di)\n\n    if w == 1 and h == 1:\n        return (x + dcx*_di, y + dcy*_di, z + dcz*_di)\n\n    (ax2, ay2, az2) = (ax//2, ay//2, az//2)\n    (bx2, by2, bz2) = (bx//2, by//2, bz//2)\n    (cx2, cy2, cz2) = (cx//2, cy//2, cz//2)\n\n    w2 = abs(ax2 + ay2 + az2)\n    h2 = abs(bx2 + by2 + bz2)\n    d2 = abs(cx2 + cy2 + cz2)\n\n    # prefer even steps\n    if (w2 % 2) and (w > 2): (ax2, ay2, az2) = (ax2 + dax, ay2 + day, az2 + daz)\n    if (h2 % 2) and (h > 2): (bx2, by2, bz2) = (bx2 + dbx, by2 + dby, bz2 + dbz)\n    if (d2 % 2) and (d > 2): (cx2, cy2, cz2) = (cx2 + dcx, cy2 + dcy, cz2 + dcz)\n\n    # wide case, split in w only\n    if (2*w > 3*h) and (2*w > 3*d):\n        nxt_idx = cur_idx + abs( (ax2 + ay2 + az2)*(bx + by + bz)*(cx + cy + cz) )\n        if (cur_idx <= dst_idx) and (dst_idx < nxt_idx):\n            return gilbert_d2xyz_r(dst_idx,cur_idx,\n                                   x, y, z,\n                                   ax2, ay2, az2,\n                                   bx, by, bz,\n                                   cx, cy, cz)\n        cur_idx = nxt_idx\n\n        return gilbert_d2xyz_r(dst_idx,cur_idx,\n                               x+ax2, y+ay2, z+az2,\n                               ax-ax2, ay-ay2, az-az2,\n                               bx, by, bz,\n                               cx, cy, cz)\n\n    # do not split in d\n    elif 3*h > 4*d:\n        nxt_idx = cur_idx + abs( (bx2 + by2 + bz2)*(cx + cy + cz)*(ax2 + ay2 + az2) )\n        if (cur_idx <= dst_idx) and (dst_idx < nxt_idx):\n            return gilbert_d2xyz_r(dst_idx,cur_idx,\n                                   x, y, z,\n                                   bx2, by2, bz2,\n                                   cx, cy, cz,\n                                   ax2, ay2, az2)\n        cur_idx = nxt_idx\n\n        nxt_idx = cur_idx + abs( (ax + ay + az)*((bx - bx2) + (by - by2) + (bz - bz2))*(cx + cy + cz) )\n        if (cur_idx <= dst_idx) and (dst_idx < nxt_idx):\n            return gilbert_d2xyz_r(dst_idx,cur_idx,\n                                   x+bx2, y+by2, z+bz2,\n                                   ax, ay, az,\n                                   bx-bx2, by-by2, bz-bz2,\n                                   cx, cy, cz)\n        cur_idx = nxt_idx\n\n        return gilbert_d2xyz_r(dst_idx,cur_idx,\n                               x+(ax-dax)+(bx2-dbx),\n                               y+(ay-day)+(by2-dby),\n                               z+(az-daz)+(bz2-dbz),\n                               -bx2, -by2, -bz2,\n                               cx, cy, cz,\n                               -(ax-ax2), -(ay-ay2), -(az-az2))\n\n    # do not split in h\n    elif 3*d > 4*h:\n        nxt_idx = cur_idx + abs( (cx2 + cy2 + cz2)*(ax2 + ay2 + az2)*(bx + by + bz) )\n        if (cur_idx <= dst_idx) and (dst_idx < nxt_idx):\n            return gilbert_d2xyz_r(dst_idx,cur_idx,\n                                   x, y, z,\n                                   cx2, cy2, cz2,\n                                   ax2, ay2, az2,\n                                   bx, by, bz)\n        cur_idx = nxt_idx\n\n        nxt_idx = cur_idx + abs( (ax + ay + az)*(bx + by + bz)*((cx - cx2) + (cy - cy2) + (cz - cz2)) )\n        if (cur_idx <= dst_idx) and (dst_idx < nxt_idx):\n            return gilbert_d2xyz_r(dst_idx,cur_idx,\n                                   x+cx2, y+cy2, z+cz2,\n                                   ax, ay, az,\n                                   bx, by, bz,\n                                   cx-cx2, cy-cy2, cz-cz2)\n        cur_idx = nxt_idx\n\n        return gilbert_d2xyz_r(dst_idx,cur_idx,\n                               x+(ax-dax)+(cx2-dcx),\n                               y+(ay-day)+(cy2-dcy),\n                               z+(az-daz)+(cz2-dcz),\n                               -cx2, -cy2, -cz2,\n                               -(ax-ax2), -(ay-ay2), -(az-az2),\n                               bx, by, bz)\n\n    # regular case, split in all w/h/d\n    else:\n        nxt_idx = cur_idx + abs( (bx2 + by2 + bz2)*(cx2 + cy2 + cz2)*(ax2 + ay2 + az2) )\n        if (cur_idx <= dst_idx) and (dst_idx < nxt_idx):\n            return gilbert_d2xyz_r(dst_idx,cur_idx,\n                                   x, y, z,\n                                   bx2, by2, bz2,\n                                   cx2, cy2, cz2,\n                                   ax2, ay2, az2)\n        cur_idx = nxt_idx\n\n        nxt_idx = cur_idx + abs( (cx + cy + cz)*(ax2 + ay2 + az2)*((bx - bx2) + (by - by2) + (bz - bz2)) )\n        if (cur_idx <= dst_idx) and (dst_idx < nxt_idx):\n            return gilbert_d2xyz_r(dst_idx,cur_idx,\n                                   x+bx2, y+by2, z+bz2,\n                                   cx, cy, cz,\n                                   ax2, ay2, az2,\n                                   bx-bx2, by-by2, bz-bz2)\n        cur_idx = nxt_idx\n\n        nxt_idx = cur_idx + abs( (ax + ay + az)*(-bx2 - by2 - bz2)*(-(cx - cx2) - (cy - cy2) - (cz - cz2)) )\n        if (cur_idx <= dst_idx) and (dst_idx < nxt_idx):\n            return gilbert_d2xyz_r(dst_idx, cur_idx,\n                                   x+(bx2-dbx)+(cx-dcx),\n                                   y+(by2-dby)+(cy-dcy),\n                                   z+(bz2-dbz)+(cz-dcz),\n                                   ax, ay, az,\n                                   -bx2, -by2, -bz2,\n                                   -(cx-cx2), -(cy-cy2), -(cz-cz2))\n        cur_idx = nxt_idx\n\n        nxt_idx = cur_idx + abs( (-cx - cy - cz)*(-(ax - ax2) - (ay - ay2) - (az - az2))*((bx - bx2) + (by - by2) + (bz - bz2)) )\n        if (cur_idx <= dst_idx) and (dst_idx < nxt_idx):\n            return gilbert_d2xyz_r(dst_idx,cur_idx,\n                                   x+(ax-dax)+bx2+(cx-dcx),\n                                   y+(ay-day)+by2+(cy-dcy),\n                                   z+(az-daz)+bz2+(cz-dcz),\n                                   -cx, -cy, -cz,\n                                   -(ax-ax2), -(ay-ay2), -(az-az2),\n                                   bx-bx2, by-by2, bz-bz2)\n        cur_idx = nxt_idx\n\n        return gilbert_d2xyz_r(dst_idx,cur_idx,\n                               x+(ax-dax)+(bx2-dbx),\n                               y+(ay-day)+(by2-dby),\n                               z+(az-daz)+(bz2-dbz),\n                               -bx2, -by2, -bz2,\n                               cx2, cy2, cz2,\n                               -(ax-ax2), -(ay-ay2), -(az-az2))\n\n\nif __name__ == \"__main__\":\n\n    import argparse\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('width', type=int)\n    parser.add_argument('height', type=int)\n    parser.add_argument('depth', type=int)\n    args = parser.parse_args()\n\n    w = args.width\n    h = args.height\n    d = args.depth\n\n    n = w*h*d\n\n    for idx in range(n):\n        (x,y,z) = gilbert_d2xyz(idx,w,h,d)\n        print(x,y,z)\n\n"
  },
  {
    "path": "models/layers/gilbert/gilbert_xy2d.py",
    "content": "#!/usr/bin/env python3\n# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2024 abetusk\n\ndef gilbert_xy2d(x, y, w, h):\n    \"\"\"\n    Generalized Hilbert ('gilbert') space-filling curve for arbitrary-sized\n    2D rectangular grids. Takes a discrete 2D coordinate and maps it to the\n    index position on the gilbert curve.\n    \"\"\"\n\n    if w >= h:\n        return gilbert_xy2d_r(0, x,y, 0,0, w,0, 0,h)\n    return gilbert_xy2d_r(0, x,y, 0,0, 0,h, w,0)\n\n\ndef sgn(x):\n    return -1 if x < 0 else (1 if x > 0 else 0)\n\n\ndef in_bounds(x, y, x_s, y_s, ax, ay, bx, by):\n\n    dx = ax + bx\n    dy = ay + by\n\n    if dx < 0:\n        if (x > x_s) or (x <= (x_s + dx)): return False\n    else:\n        if (x < x_s) or (x >= (x_s + dx)): return False\n\n    if dy < 0:\n        if (y > y_s) or (y <= (y_s + dy)): return False\n    else:\n        if (y < y_s) or (y >= (y_s + dy)): return False\n\n    return True\n\n\ndef gilbert_xy2d_r(cur_idx, x_dst, y_dst, x, y, ax, ay, bx, by):\n\n    w = abs(ax + ay)\n    h = abs(bx + by)\n\n    (dax, day) = (sgn(ax), sgn(ay)) # unit major direction\n    (dbx, dby) = (sgn(bx), sgn(by)) # unit orthogonal direction\n\n    dx = dax + dbx\n    dy = day + dby\n\n    if h == 1:\n        if (dax==0): return cur_idx + (dy*(y_dst - y))\n        return cur_idx + (dx*(x_dst - x))\n\n    if w == 1:\n        if (dbx==0): return cur_idx + (dy*(y_dst - y))\n        return cur_idx + (dx*(x_dst - x))\n\n    (ax2, ay2) = (ax//2, ay//2)\n    (bx2, by2) = (bx//2, by//2)\n\n    w2 = abs(ax2 + ay2)\n    h2 = abs(bx2 + by2)\n\n    if 2*w > 3*h:\n        if (w2 % 2) and (w > 2):\n            # prefer even steps\n            (ax2, ay2) = (ax2 + dax, ay2 + day)\n\n        if in_bounds(x_dst, y_dst, x, y, ax2, ay2, bx, by):\n            return gilbert_xy2d_r(cur_idx, x_dst, y_dst, x, y, ax2, ay2, bx, by)\n\n        cur_idx += abs((ax2 + ay2)*(bx + by))\n        return gilbert_xy2d_r(cur_idx, x_dst, y_dst, x+ax2, y+ay2, ax-ax2, ay-ay2, bx, by)\n\n    else:\n        if (h2 % 2) and (h > 2):\n            # prefer even steps\n            (bx2, by2) = (bx2 + dbx, by2 + dby)\n\n        # standard case: one step up, one long horizontal, one step down\n        if in_bounds(x_dst, y_dst, x, y, bx2, by2, ax2, ay2):\n            return gilbert_xy2d_r(cur_idx, x_dst,y_dst, x,y, bx2,by2, ax2,ay2)\n        cur_idx += abs((bx2 + by2)*(ax2 + ay2))\n\n        if in_bounds(x_dst, y_dst, x+bx2, y+by2, ax, ay, bx-bx2, by-by2):\n            return gilbert_xy2d_r(cur_idx, x_dst,y_dst, x+bx2,y+by2, ax,ay, bx-bx2,by-by2)\n        cur_idx += abs((ax + ay)*((bx - bx2) + (by - by2)))\n\n        return gilbert_xy2d_r(cur_idx, x_dst,y_dst,\n                              x+(ax-dax)+(bx2-dbx),\n                              y+(ay-day)+(by2-dby),\n                              -bx2, -by2,\n                              -(ax-ax2), -(ay-ay2))\n\nif __name__ == \"__main__\":\n\n    import argparse\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('width', type=int)\n    parser.add_argument('height', type=int)\n    args = parser.parse_args()\n\n    width = args.width\n    height = args.height\n\n    for x in range(width):\n      for y in range(height):\n        idx = gilbert_xy2d(x,y, width,height)\n        print(idx,x,y)\n\n"
  },
  {
    "path": "models/layers/gilbert/gilbert_xyz2d.py",
    "content": "#!/usr/bin/env python3\n# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2024 abetusk\n\ndef gilbert_xyz2d(x, y, z, width, height, depth):\n    \"\"\"\n    Generalized Hilbert ('Gilbert') space-filling curve for arbitrary-sized\n    3D rectangular grids. Generates discrete 3D coordinates to fill a cuboid\n    of size (width x height x depth). Even sizes are recommended in 3D.\n    \"\"\"\n\n    if width >= height and width >= depth:\n       return gilbert_xyz2d_r(0,x,y,z,\n                              0, 0, 0,\n                              width, 0, 0,\n                              0, height, 0,\n                              0, 0, depth)\n\n    elif height >= width and height >= depth:\n       return gilbert_xyz2d_r(0,x,y,z,\n                              0, 0, 0,\n                              0, height, 0,\n                              width, 0, 0,\n                              0, 0, depth)\n\n    else: # depth >= width and depth >= height\n       return gilbert_xyz2d_r(0,x,y,z,\n                              0, 0, 0,\n                              0, 0, depth,\n                              width, 0, 0,\n                              0, height, 0)\n\n\ndef sgn(x):\n    return -1 if x < 0 else (1 if x > 0 else 0)\n\ndef in_bounds(x, y, z, x_s, y_s, z_s, ax, ay, az, bx, by, bz, cx, cy, cz):\n\n    dx = ax + bx + cx\n    dy = ay + by + cy\n    dz = az + bz + cz\n\n    if dx < 0:\n        if (x > x_s) or (x <= (x_s + dx)): return False\n    else:\n        if (x < x_s) or (x >= (x_s + dx)): return False\n\n    if dy < 0:\n        if (y > y_s) or (y <= (y_s + dy)): return False\n    else:\n        if (y < y_s) or (y >= (y_s + dy)): return False\n\n    if dz <0:\n        if (z > z_s) or (z <= (z_s + dz)): return False\n    else:\n        if (z < z_s) or (z >= (z_s + dz)): return False\n\n    return True\n\n\ndef gilbert_xyz2d_r(cur_idx,\n                    x_dst,y_dst,z_dst,\n                    x, y, z,\n                    ax, ay, az,\n                    bx, by, bz,\n                    cx, cy, cz):\n\n    w = abs(ax + ay + az)\n    h = abs(bx + by + bz)\n    d = abs(cx + cy + cz)\n\n    (dax, day, daz) = (sgn(ax), sgn(ay), sgn(az)) # unit major direction (\"right\")\n    (dbx, dby, dbz) = (sgn(bx), sgn(by), sgn(bz)) # unit ortho direction (\"forward\")\n    (dcx, dcy, dcz) = (sgn(cx), sgn(cy), sgn(cz)) # unit ortho direction (\"up\")\n\n    # trivial row/column fills\n    if h == 1 and d == 1:\n        return cur_idx + (dax*(x_dst - x)) + (day*(y_dst - y)) + (daz*(z_dst - z))\n\n    if w == 1 and d == 1:\n        return cur_idx + (dbx*(x_dst - x)) + (dby*(y_dst - y)) + (dbz*(z_dst - z))\n\n    if w == 1 and h == 1:\n        return cur_idx + (dcx*(x_dst - x)) + (dcy*(y_dst - y)) + (dcz*(z_dst - z))\n\n    (ax2, ay2, az2) = (ax//2, ay//2, az//2)\n    (bx2, by2, bz2) = (bx//2, by//2, bz//2)\n    (cx2, cy2, cz2) = (cx//2, cy//2, cz//2)\n\n    w2 = abs(ax2 + ay2 + az2)\n    h2 = abs(bx2 + by2 + bz2)\n    d2 = abs(cx2 + cy2 + cz2)\n\n    # prefer even steps\n    if (w2 % 2) and (w > 2):\n       (ax2, ay2, az2) = (ax2 + dax, ay2 + day, az2 + daz)\n\n    if (h2 % 2) and (h > 2):\n       (bx2, by2, bz2) = (bx2 + dbx, by2 + dby, bz2 + dbz)\n\n    if (d2 % 2) and (d > 2):\n       (cx2, cy2, cz2) = (cx2 + dcx, cy2 + dcy, cz2 + dcz)\n\n    # wide case, split in w only\n    if (2*w > 3*h) and (2*w > 3*d):\n        if in_bounds(x_dst,y_dst,z_dst,\n                     x,y,z,\n                     ax2,ay2,az2,\n                     bx,by,bz,\n                     cx,cy,cz):\n            return gilbert_xyz2d_r(cur_idx,\n                                   x_dst,y_dst,z_dst,\n                                   x, y, z,\n                                   ax2, ay2, az2,\n                                   bx, by, bz,\n                                   cx, cy, cz)\n        cur_idx += abs( (ax2 + ay2 + az2)*(bx + by + bz)*(cx + cy + cz) )\n\n        return gilbert_xyz2d_r(cur_idx,\n                               x_dst,y_dst,z_dst,\n                               x+ax2, y+ay2, z+az2,\n                               ax-ax2, ay-ay2, az-az2,\n                               bx, by, bz,\n                               cx, cy, cz)\n\n    # do not split in d\n    elif 3*h > 4*d:\n        if in_bounds(x_dst,y_dst,z_dst,\n                     x,y,z,\n                     bx2,by2,bz2,\n                     cx,cy,cz,\n                     ax2,ay2,az2):\n            return gilbert_xyz2d_r(cur_idx,\n                                   x_dst,y_dst,z_dst,\n                                   x, y, z,\n                                   bx2, by2, bz2,\n                                   cx, cy, cz,\n                                   ax2, ay2, az2)\n        cur_idx += abs( (bx2 + by2 + bz2)*(cx + cy + cz)*(ax2 + ay2 + az2) )\n\n        if in_bounds(x_dst,y_dst,z_dst,\n                     x+bx2,y+by2,z+bz2,\n                     ax,ay,az,\n                     bx-bx2,by-by2,bz-bz2,\n                     cx,cy,cz):\n            return gilbert_xyz2d_r(cur_idx,\n                                   x_dst,y_dst,z_dst,\n                                   x+bx2, y+by2, z+bz2,\n                                   ax, ay, az,\n                                   bx-bx2, by-by2, bz-bz2,\n                                   cx, cy, cz)\n        cur_idx += abs( (ax + ay + az)*((bx - bx2) + (by - by2) + (bz - bz2))*(cx + cy + cz) )\n\n        return gilbert_xyz2d_r(cur_idx,\n                               x_dst,y_dst,z_dst,\n                               x+(ax-dax)+(bx2-dbx),\n                               y+(ay-day)+(by2-dby),\n                               z+(az-daz)+(bz2-dbz),\n                               -bx2, -by2, -bz2,\n                               cx, cy, cz,\n                               -(ax-ax2), -(ay-ay2), -(az-az2))\n\n    # do not split in h\n    elif 3*d > 4*h:\n        if in_bounds(x_dst,y_dst,z_dst,\n                     x,y,z,\n                     cx2,cy2,cz2,\n                     ax2,ay2,az2, bx,by,bz):\n            return gilbert_xyz2d_r(cur_idx,\n                                   x_dst,y_dst,z_dst,\n                                   x, y, z,\n                                   cx2, cy2, cz2,\n                                   ax2, ay2, az2,\n                                   bx, by, bz)\n        cur_idx += abs( (cx2 + cy2 + cz2)*(ax2 + ay2 + az2)*(bx + by + bz) )\n\n        if in_bounds(x_dst,y_dst,z_dst,\n                     x+cx2,y+cy2,z+cz2,\n                     ax,ay,az, bx,by,bz,\n                     cx-cx2,cy-cy2,cz-cz2):\n            return gilbert_xyz2d_r(cur_idx,\n                                   x_dst,y_dst,z_dst,\n                                   x+cx2, y+cy2, z+cz2,\n                                   ax, ay, az,\n                                   bx, by, bz,\n                                   cx-cx2, cy-cy2, cz-cz2)\n        cur_idx += abs( (ax + ay + az)*(bx + by + bz)*((cx - cx2) + (cy - cy2) + (cz - cz2)) )\n\n        return gilbert_xyz2d_r(cur_idx,\n                               x_dst,y_dst,z_dst,\n                               x+(ax-dax)+(cx2-dcx),\n                               y+(ay-day)+(cy2-dcy),\n                               z+(az-daz)+(cz2-dcz),\n                               -cx2, -cy2, -cz2,\n                               -(ax-ax2), -(ay-ay2), -(az-az2),\n                               bx, by, bz)\n\n    # regular case, split in all w/h/d\n    if in_bounds(x_dst,y_dst,z_dst,\n                 x,y,z,\n                 bx2,by2,bz2,\n                 cx2,cy2,cz2,\n                 ax2,ay2,az2):\n        return gilbert_xyz2d_r(cur_idx,x_dst,y_dst,z_dst,\n                              x, y, z,\n                              bx2, by2, bz2,\n                              cx2, cy2, cz2,\n                              ax2, ay2, az2)\n    cur_idx += abs( (bx2 + by2 + bz2)*(cx2 + cy2 + cz2)*(ax2 + ay2 + az2) )\n\n    if in_bounds(x_dst,y_dst,z_dst,\n                 x+bx2, y+by2, z+bz2,\n                 cx, cy, cz,\n                 ax2, ay2, az2,\n                 bx-bx2, by-by2, bz-bz2):\n        return gilbert_xyz2d_r(cur_idx,\n                              x_dst,y_dst,z_dst,\n                              x+bx2, y+by2, z+bz2,\n                              cx, cy, cz,\n                              ax2, ay2, az2,\n                              bx-bx2, by-by2, bz-bz2)\n    cur_idx += abs( (cx + cy + cz)*(ax2 + ay2 + az2)*((bx - bx2) + (by - by2) + (bz - bz2)) )\n\n    if in_bounds(x_dst,y_dst,z_dst,\n                 x+(bx2-dbx)+(cx-dcx),\n                 y+(by2-dby)+(cy-dcy),\n                 z+(bz2-dbz)+(cz-dcz),\n                 ax, ay, az,\n                 -bx2, -by2, -bz2,\n                 -(cx-cx2), -(cy-cy2), -(cz-cz2)):\n        return gilbert_xyz2d_r(cur_idx,\n                               x_dst,y_dst,z_dst,\n                               x+(bx2-dbx)+(cx-dcx),\n                               y+(by2-dby)+(cy-dcy),\n                               z+(bz2-dbz)+(cz-dcz),\n                               ax, ay, az,\n                               -bx2, -by2, -bz2,\n                               -(cx-cx2), -(cy-cy2), -(cz-cz2))\n    cur_idx += abs( (ax + ay + az)*(-bx2 - by2 - bz2)*(-(cx - cx2) - (cy - cy2) - (cz - cz2)) )\n\n    if in_bounds(x_dst,y_dst,z_dst,\n                 x+(ax-dax)+bx2+(cx-dcx),\n                 y+(ay-day)+by2+(cy-dcy),\n                 z+(az-daz)+bz2+(cz-dcz),\n                 -cx, -cy, -cz,\n                 -(ax-ax2), -(ay-ay2), -(az-az2),\n                 bx-bx2, by-by2, bz-bz2):\n        return gilbert_xyz2d_r(cur_idx,\n                               x_dst,y_dst,z_dst,\n                               x+(ax-dax)+bx2+(cx-dcx),\n                               y+(ay-day)+by2+(cy-dcy),\n                               z+(az-daz)+bz2+(cz-dcz),\n                               -cx, -cy, -cz,\n                               -(ax-ax2), -(ay-ay2), -(az-az2),\n                               bx-bx2, by-by2, bz-bz2)\n    cur_idx += abs( (-cx - cy - cz)*(-(ax - ax2) - (ay - ay2) - (az - az2))*((bx - bx2) + (by - by2) + (bz - bz2)) )\n\n    return gilbert_xyz2d_r(cur_idx,\n                           x_dst,y_dst,z_dst,\n                           x+(ax-dax)+(bx2-dbx),\n                           y+(ay-day)+(by2-dby),\n                           z+(az-daz)+(bz2-dbz),\n                           -bx2, -by2, -bz2,\n                           cx2, cy2, cz2,\n                           -(ax-ax2), -(ay-ay2), -(az-az2))\n\n\nif __name__ == \"__main__\":\n\n    import argparse\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('width', type=int)\n    parser.add_argument('height', type=int)\n    parser.add_argument('depth', type=int)\n    args = parser.parse_args()\n\n    w = args.width\n    h = args.height\n    d = args.depth\n\n    n = w*h*d\n\n    for x in range(w):\n        for y in range(h):\n            for z in range(d):\n                idx = gilbert_xyz2d(x,y,z,w,h,d)\n                print(idx,x,y,z)\n"
  },
  {
    "path": "models/layers/gilbert/plotpath.m",
    "content": "# Octave helper function to plot a 2D or 3D colored curve\n\nfunction h = plotpath(P)\n\n    x = P(:,1)';\n    y = P(:,2)';\n\n    if (size(P,2) >= 3)\n        z = P(:,3)';\n    else\n        z = zeros(size(x));\n    endif\n\n    col = 1:size(x,2);\n\n    colormap jet;\n\n    h = surface([x;x],[y;y],[z;z],[col;col],...\n                'facecolor','none',...\n                'edgecolor','interp',...\n                'linewidth',2);\n"
  },
  {
    "path": "models/layers/gilbert/ports/Makefile",
    "content": "\nCC := gcc\nCFLAGS :=\nOPT := -O3\n\nSRCFILES := gilbert.c\n\nall: gilbert\n\ngilbert: gilbert.c\n\t$(CC) gilbert.c -o gilbert $(CFLAGS) $(OPT)\n\n.PHONY: clean\n\nclean:\n\trm -f gilbert\n\n"
  },
  {
    "path": "models/layers/gilbert/ports/gilbert.c",
    "content": "// SPDX-License-Identifier: BSD-2-Clause\n// Copyright (c) 2024 abetusk\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <math.h>\n\nint gilbert_d2xy_r(int dst_idx, int cur_idx,\n                   int *xres, int *yres,\n                   int ax,int ay,\n                   int bx,int by );\n\nint gilbert_xy2d_r(int cur_idx,\n                   int x_dst, int y_dst,\n                   int x, int y,\n                   int ax, int ay,\n                   int bx,int by );\n\nint gilbert_xy2d(int x, int y, int w, int h) {\n  if (w >= h) {\n    return gilbert_xy2d_r(0, x,y, 0,0, w,0, 0,h);\n  }\n  return gilbert_xy2d_r(0, x,y, 0,0, 0,h, w,0);\n}\n\nint gilbert_d2xy(int *x, int *y, int idx,int w,int h) {\n  *x = 0;\n  *y = 0;\n\n  if (w >= h) {\n    return gilbert_d2xy_r(idx,0, x,y, w,0, 0,h);\n  }\n  return gilbert_d2xy_r(idx,0, x,y, 0,h, w,0);\n}\n\nint gilbert_d2xyz_r(int dst_idx, int cur_idx,\n                    int *x, int *y, int *z,\n                    int ax, int ay, int az,\n                    int bx, int by, int bz,\n                    int cx, int cy, int cz);\n\nint gilbert_xyz2d_r(int cur_idx,\n                   int x_dst, int y_dst, int z_dst,\n                   int x,  int y,  int z,\n                   int ax, int ay, int az,\n                   int bx, int by, int bz,\n                   int cx, int cy, int cz);\n\n\nint gilbert_xyz2d(int x, int y, int z, int width, int height, int depth) {\n  if ((width >= height) && (width >= depth)) {\n    return gilbert_xyz2d_r(0,x,y,z,\n                           0, 0, 0,\n                           width, 0, 0,\n                           0, height, 0,\n                           0, 0, depth);\n  }\n  else if ((height >= width) && (height >= depth)) {\n    return gilbert_xyz2d_r(0,x,y,z,\n                           0, 0, 0,\n                           0, height, 0,\n                           width, 0, 0,\n                           0, 0, depth);\n  }\n\n  // depth >= width and depth >= height\n  return gilbert_xyz2d_r(0,x,y,z,\n                         0, 0, 0,\n                         0, 0, depth,\n                         width, 0, 0,\n                         0, height, 0);\n}\n\nint gilbert_d2xyz(int *x, int *y, int *z, int idx, int width, int height, int depth) {\n\n  *x = 0;\n  *y = 0;\n  *z = 0;\n\n  if ((width >= height) && (width >= depth)) {\n    return gilbert_d2xyz_r(idx, 0,\n                           x,y,z,\n                           width, 0, 0,\n                           0, height, 0,\n                           0, 0, depth);\n  }\n\n  else if ((height >= width) && (height >= depth)) {\n    return gilbert_d2xyz_r(idx, 0,\n                           x,y,z,\n                           0, height, 0,\n                           width, 0, 0,\n                           0, 0, depth);\n  }\n\n  // depth >= width and depth >= height\n  return gilbert_d2xyz_r(idx, 0,\n                         x,y,z,\n                         0, 0, depth,\n                         width, 0, 0,\n                         0, height, 0);\n}\n\n\nstatic int sgn(int x) {\n  if (x < 0) { return -1; }\n  if (x > 0) { return  1; }\n  return 0;\n}\n\nint in_bounds2(int x,  int y,\n               int x_s,int y_s,\n               int ax, int ay,\n               int bx, int by) {\n  int dx, dy;\n\n  dx = ax + bx;\n  dy = ay + by;\n\n  if (dx < 0) {\n    if ((x > x_s) || (x <= (x_s + dx))) { return 0; }\n  }\n  else {\n    if ((x < x_s) || (x >= (x_s + dx))) { return 0; }\n  }\n\n  if (dy < 0) {\n    if ((y > y_s) || (y <= (y_s + dy))) { return 0; }\n  }\n  else {\n    if ((y < y_s) || (y >= (y_s + dy))) { return 0; }\n  }\n\n  return 1;\n}\n\nint in_bounds3(int x,  int y,  int z,\n               int x_s,int y_s,int z_s,\n               int ax, int ay, int az,\n               int bx, int by, int bz,\n               int cx, int cy, int cz) {\n  int dx, dy, dz;\n\n  dx = ax + bx + cx;\n  dy = ay + by + cy;\n  dz = az + bz + cz;\n\n  if (dx < 0) {\n    if ((x > x_s) || (x <= (x_s + dx))) { return 0; }\n  }\n  else {\n    if ((x < x_s) || (x >= (x_s + dx))) { return 0; }\n  }\n\n  if (dy < 0) {\n    if ((y > y_s) || (y <= (y_s + dy))) { return 0; }\n  }\n  else {\n    if ((y < y_s) || (y >= (y_s + dy))) { return 0; }\n  }\n\n  if (dz < 0) {\n    if ((z > z_s) || (z <= (z_s + dz))) { return 0; }\n  }\n  else {\n    if ((z < z_s) || (z >= (z_s + dz))) { return 0; }\n  }\n\n  return 1;\n}\n\n\n\nint gilbert_d2xy_r(int dst_idx, int cur_idx,\n                   int *xres, int *yres,\n                   int ax,int ay,\n                   int bx,int by ) {\n  static int max_iter = 0;\n\n  int nxt_idx;\n  int w, h, x, y,\n      dax, day,\n      dbx, dby,\n      di;\n  int ax2, ay2, bx2, by2, w2, h2;\n\n  if (max_iter > 100000) { return -1; }\n  max_iter++;\n\n  w = abs(ax + ay);\n  h = abs(bx + by);\n\n  x = *xres;\n  y = *yres;\n\n  // unit major direction\n  dax = sgn(ax);\n  day = sgn(ay);\n\n  // unit orthogonal direction\n  dbx = sgn(bx);\n  dby = sgn(by);\n\n  di = dst_idx - cur_idx;\n\n  if (h == 1) {\n    *xres = x + dax*di;\n    *yres = y + day*di;\n    return 0;\n  }\n\n  if (w == 1) {\n    *xres = x + dbx*di;\n    *yres = y + dby*di;\n    return 0;\n  }\n\n  // floor function\n  ax2 = (int)floor((double)ax/2.0);\n  ay2 = (int)floor((double)ay/2.0);\n  bx2 = (int)floor((double)bx/2.0);\n  by2 = (int)floor((double)by/2.0);\n\n  w2 = abs(ax2 + ay2);\n  h2 = abs(bx2 + by2);\n\n  if ((2*w) > (3*h)) {\n    if ((w2 % 2) && (w > 2)) {\n      // prefer even steps\n      ax2 += dax;\n      ay2 += day;\n    }\n\n    // long case: split in two parts only\n    nxt_idx = cur_idx + abs((ax2 + ay2)*(bx + by));\n    if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n      *xres = x;\n      *yres = y;\n      return gilbert_d2xy_r(dst_idx, cur_idx,  xres, yres, ax2, ay2, bx, by);\n    }\n    cur_idx = nxt_idx;\n\n    *xres = x + ax2;\n    *yres = y + ay2;\n    return gilbert_d2xy_r(dst_idx, cur_idx, xres, yres, ax-ax2, ay-ay2, bx, by);\n  }\n\n  if ((h2 % 2) && (h > 2)) {\n    // prefer even steps\n    bx2 += dbx;\n    by2 += dby;\n  }\n\n  // standard case: one step up, one long horizontal, one step down\n  nxt_idx = cur_idx + abs((bx2 + by2)*(ax2 + ay2));\n  if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n    *xres = x;\n    *yres = y;\n    return gilbert_d2xy_r(dst_idx, cur_idx, xres,yres, bx2,by2, ax2,ay2);\n  }\n  cur_idx = nxt_idx;\n\n  nxt_idx = cur_idx + abs((ax + ay)*((bx - bx2) + (by - by2)));\n  if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n    *xres = x + bx2;\n    *yres = y + by2;\n    return gilbert_d2xy_r(dst_idx, cur_idx, xres,yres, ax,ay, bx-bx2,by-by2);\n  }\n  cur_idx = nxt_idx;\n\n  *xres = x + (ax - dax) + (bx2 - dbx);\n  *yres = y + (ay - day) + (by2 - dby);\n  return gilbert_d2xy_r(dst_idx, cur_idx,\n                        xres,yres,\n                        -bx2, -by2,\n                        -(ax-ax2), -(ay-ay2));\n}\n\nint gilbert_xy2d_r(int cur_idx,\n                   int x_dst, int y_dst,\n                   int x, int y,\n                   int ax, int ay,\n                   int bx,int by ) {\n  int dax, day, dbx, dby,\n      ax2, ay2, bx2, by2;\n  int w, h, w2, h2;\n  int dx, dy;\n\n  w = abs(ax + ay);\n  h = abs(bx + by);\n\n  // unit major direction\n  dax = sgn(ax);\n  day = sgn(ay);\n\n  // unit orthogonal direction\n  dbx = sgn(bx);\n  dby = sgn(by);\n\n  dx = dax + dbx;\n  dy = day + dby;\n\n  if (h == 1) {\n    if (dax == 0) { return cur_idx + (dy*(y_dst - y)); }\n    return cur_idx + (dx*(x_dst - x));\n  }\n\n  if (w == 1) {\n    if (dbx == 0) { return cur_idx + (dy*(y_dst - y)); }\n    return cur_idx + (dx*(x_dst - x));\n  }\n\n  ax2 = (int)floor((double)ax/2.0);\n  ay2 = (int)floor((double)ay/2.0);\n  bx2 = (int)floor((double)bx/2.0);\n  by2 = (int)floor((double)by/2.0);\n\n  w2 = abs(ax2 + ay2);\n  h2 = abs(bx2 + by2);\n\n  if ((2*w) > (3*h)) {\n    if ((w2 % 2) && (w > 2)) {\n      // prefer even steps\n      ax2 += dax;\n      ay2 += day;\n    }\n\n    if (in_bounds2( x_dst, y_dst, x,y, ax2,ay2, bx,by )) {\n      return gilbert_xy2d_r(cur_idx, x_dst, y_dst, x, y, ax2, ay2, bx, by);\n    }\n    cur_idx += abs((ax2 + ay2)*(bx + by));\n\n    return gilbert_xy2d_r(cur_idx, x_dst, y_dst, x+ax2, y+ay2, ax-ax2, ay-ay2, bx, by);\n  }\n\n  if ((h2 % 2) && (h > 2)) {\n    // prefer even steps\n    bx2 += dbx;\n    by2 += dby;\n  }\n\n  // standard case: one step up, one long horizontal, one step down\n  if (in_bounds2( x_dst,y_dst, x,y, bx2,by2, ax2,ay2 )) {\n    return gilbert_xy2d_r(cur_idx, x_dst,y_dst, x,y, bx2,by2, ax2,ay2);\n  }\n  cur_idx += abs((bx2 + by2)*(ax2 + ay2));\n\n  if (in_bounds2( x_dst,y_dst, x+bx2,y+by2, ax,ay, bx-bx2,by-by2)) {\n    return gilbert_xy2d_r(cur_idx, x_dst,y_dst, x+bx2,y+by2, ax,ay, bx-bx2,by-by2);\n  }\n  cur_idx += abs((ax + ay)*((bx - bx2) + (by - by2)));\n\n  return gilbert_xy2d_r(cur_idx, x_dst,y_dst,\n                        x+(ax-dax)+(bx2-dbx),\n                        y+(ay-day)+(by2-dby),\n                        -bx2, -by2,\n                        -(ax-ax2), -(ay-ay2));\n}\n\n\n\nint gilbert_d2xyz_r(int dst_idx, int cur_idx,\n                    int *xres, int *yres, int *zres,\n                    int ax, int ay, int az,\n                    int bx, int by, int bz,\n                    int cx, int cy, int cz) {\n  int x, y, z;\n  int _dx, _dy, _dz, _di;\n  int nxt_idx;\n\n  int w, h, d;\n  int w2, h2, d2;\n\n  int dax, day, daz,\n      dbx, dby, dbz,\n      dcx, dcy, dcz;\n  int ax2, ay2, az2,\n      bx2, by2, bz2,\n      cx2, cy2, cz2;\n\n  x = *xres;\n  y = *yres;\n  z = *zres;\n\n  w = abs(ax + ay + az);\n  h = abs(bx + by + bz);\n  d = abs(cx + cy + cz);\n\n  dax = sgn(ax); day = sgn(ay); daz = sgn(az);  // unit major direction \"right\"\n  dbx = sgn(bx); dby = sgn(by); dbz = sgn(bz);  // unit ortho direction \"forward\"\n  dcx = sgn(cx); dcy = sgn(cy); dcz = sgn(cz);  // unit ortho direction \"up\"\n\n  _dx = dax + dbx + dcx;\n  _dy = day + dby + dcy;\n  _dz = daz + dbz + dcz;\n  _di = dst_idx - cur_idx;\n\n  // trivial row/column fills\n  if ((h == 1) && (d == 1)) {\n    *xres = x + dax*_di;\n    *yres = y + day*_di;\n    *zres = z + daz*_di;\n    return 0;\n  }\n\n  if ((w == 1) && (d == 1)) {\n    *xres = x + dbx*_di;\n    *yres = y + dby*_di;\n    *zres = z + dbz*_di;\n    return 0;\n  }\n\n  if ((w == 1) && (h == 1)) {\n    *xres = x + dcx*_di;\n    *yres = y + dcy*_di;\n    *zres = z + dcz*_di;\n    return 0;\n  }\n\n  ax2 = (int)floor((double)ax/2.0);\n  ay2 = (int)floor((double)ay/2.0);\n  az2 = (int)floor((double)az/2.0);\n\n  bx2 = (int)floor((double)bx/2.0);\n  by2 = (int)floor((double)by/2.0);\n  bz2 = (int)floor((double)bz/2.0);\n\n  cx2 = (int)floor((double)cx/2.0);\n  cy2 = (int)floor((double)cy/2.0);\n  cz2 = (int)floor((double)cz/2.0);\n\n  w2 = abs(ax2 + ay2 + az2);\n  h2 = abs(bx2 + by2 + bz2);\n  d2 = abs(cx2 + cy2 + cz2);\n\n  // prefer even steps\n  if ((w2 % 2) && (w > 2)) { ax2 += dax; ay2 += day; az2 += daz; }\n  if ((h2 % 2) && (h > 2)) { bx2 += dbx; by2 += dby; bz2 += dbz; }\n  if ((d2 % 2) && (d > 2)) { cx2 += dcx; cy2 += dcy; cz2 += dcz; }\n\n  // wide case, split in w only\n  if (((2*w) > (3*h)) && ((2*w) > (3*d))) {\n    nxt_idx = cur_idx + abs( (ax2 + ay2 + az2)*(bx + by + bz)*(cx + cy + cz) );\n    if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n      *xres = x;\n      *yres = y;\n      *zres = z;\n      return gilbert_d2xyz_r(dst_idx,cur_idx,\n                             xres,yres,zres,\n                             ax2, ay2, az2,\n                             bx, by, bz,\n                             cx, cy, cz);\n    }\n    cur_idx = nxt_idx;\n\n\n    *xres = x + ax2;\n    *yres = y + ay2;\n    *zres = z + az2;\n    return gilbert_d2xyz_r(dst_idx,cur_idx,\n                           xres,yres,zres,\n                           ax-ax2, ay-ay2, az-az2,\n                           bx, by, bz,\n                           cx, cy, cz);\n  }\n\n  // do not split in d\n  else if ((3*h) > (4*d)) {\n    nxt_idx = cur_idx + abs( (bx2 + by2 + bz2)*(cx + cy + cz)*(ax2 + ay2 + az2) );\n    if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n      *xres = x;\n      *yres = y;\n      *zres = z;\n      return gilbert_d2xyz_r(dst_idx,cur_idx,\n                             xres,yres,zres,\n                             bx2, by2, bz2,\n                             cx, cy, cz,\n                             ax2, ay2, az2);\n    }\n    cur_idx = nxt_idx;\n\n    nxt_idx = cur_idx + abs( (ax + ay + az)*((bx - bx2) + (by - by2) + (bz - bz2))*(cx + cy + cz) );\n    if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n      *xres = x + bx2;\n      *yres = y + by2;\n      *zres = z + bz2;\n      return gilbert_d2xyz_r(dst_idx,cur_idx,\n                             xres,yres,zres,\n                             ax, ay, az,\n                             bx-bx2, by-by2, bz-bz2,\n                             cx, cy, cz);\n    }\n    cur_idx = nxt_idx;\n\n    *xres = x + (ax - dax) + (bx2 - dbx);\n    *yres = y + (ay - day) + (by2 - dby);\n    *zres = z + (az - daz) + (bz2 - dbz);\n\n    return gilbert_d2xyz_r(dst_idx,cur_idx,\n                           xres,yres,zres,\n                           -bx2, -by2, -bz2,\n                           cx, cy, cz,\n                           -(ax-ax2), -(ay-ay2), -(az-az2));\n  }\n\n  // do not split in h\n  else if ((3*d) > (4*h)) {\n    nxt_idx = cur_idx + abs( (cx2 + cy2 + cz2)*(ax2 + ay2 + az2)*(bx + by + bz) );\n    if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n      *xres = x;\n      *yres = y;\n      *zres = z;\n      return gilbert_d2xyz_r(dst_idx,cur_idx,\n                             xres,yres,zres,\n                             cx2, cy2, cz2,\n                             ax2, ay2, az2,\n                             bx, by, bz);\n    }\n    cur_idx = nxt_idx;\n\n    nxt_idx = cur_idx + abs( (ax + ay + az)*(bx + by + bz)*((cx-cx2) + (cy-cy2) + (cz-cz2)) );\n    if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n      *xres = x + cx2;\n      *yres = y + cy2;\n      *zres = z + cz2;\n      return gilbert_d2xyz_r(dst_idx,cur_idx,\n                             xres,yres,zres,\n                             ax, ay, az,\n                             bx, by, bz,\n                             cx-cx2, cy-cy2, cz-cz2);\n    }\n    cur_idx = nxt_idx;\n\n    *xres = x + (ax - dax) + (cx2 - dcx);\n    *yres = y + (ay - day) + (cy2 - dcy);\n    *zres = z + (az - daz) + (cz2 - dcz);\n\n    return gilbert_d2xyz_r(dst_idx,cur_idx,\n                           xres,yres,zres,\n                           -cx2, -cy2, -cz2,\n                           -(ax-ax2), -(ay-ay2), -(az-az2),\n                           bx, by, bz);\n\n  }\n\n  // regular case, split in all w/h/d\n  nxt_idx = cur_idx + abs( (bx2 + by2 + bz2)*(cx2 + cy2 + cz2)*(ax2 + ay2 + az2) );\n  if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n    *xres = x;\n    *yres = y;\n    *zres = z;\n    return gilbert_d2xyz_r(dst_idx,cur_idx,\n                           xres,yres,zres,\n                           bx2, by2, bz2,\n                           cx2, cy2, cz2,\n                           ax2, ay2, az2);\n  }\n  cur_idx = nxt_idx;\n\n  nxt_idx = cur_idx + abs( (cx + cy + cz)*(ax2 + ay2 + az2)*((bx-bx2) + (by-by2) + (bz-bz2)) );\n  if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n    *xres = x + bx2;\n    *yres = y + by2;\n    *zres = z + bz2;\n    return gilbert_d2xyz_r(dst_idx,cur_idx,\n                           xres,yres,zres,\n                           cx, cy, cz,\n                           ax2, ay2, az2,\n                           bx-bx2, by-by2, bz-bz2);\n  }\n  cur_idx = nxt_idx;\n\n  nxt_idx = cur_idx + abs( (ax + ay + az)*( -bx2 - by2 - bz2)*( -(cx - cx2) - (cy - cy2) - (cz - cz2)) );\n  if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n    *xres = x + (bx2 - dbx) + (cx - dcx);\n    *yres = y + (by2 - dby) + (cy - dcy);\n    *zres = z + (bz2 - dbz) + (cz - dcz);\n    return gilbert_d2xyz_r(dst_idx, cur_idx,\n                           xres,yres,zres,\n                           ax, ay, az,\n                           -bx2, -by2, -bz2,\n                           -(cx-cx2), -(cy-cy2), -(cz-cz2));\n  }\n  cur_idx = nxt_idx;\n\n  nxt_idx = cur_idx + abs( ( -cx - cy - cz)*( -(ax - ax2) - (ay - ay2) - (az - az2))*((bx - bx2) + (by - by2) + (bz - bz2)) );\n  if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n    *xres = x + (ax - dax) + bx2 + (cx - dcx);\n    *yres = y + (ay - day) + by2 + (cy - dcy);\n    *zres = z + (az - daz) + bz2 + (cz - dcz);\n    return gilbert_d2xyz_r(dst_idx,cur_idx,\n                           xres,yres,zres,\n                           -cx, -cy, -cz,\n                           -(ax-ax2), -(ay-ay2), -(az-az2),\n                           bx-bx2, by-by2, bz-bz2);\n  }\n  cur_idx = nxt_idx;\n\n  *xres = x + (ax - dax) + (bx2 - dbx);\n  *yres = y + (ay - day) + (by2 - dby);\n  *zres = z + (az - daz) + (bz2 - dbz);\n  return gilbert_d2xyz_r(dst_idx,cur_idx,\n                         xres,yres,zres,\n                         -bx2, -by2, -bz2,\n                         cx2, cy2, cz2,\n                         -(ax-ax2), -(ay-ay2), -(az-az2));\n\n}\n\n\n\nint gilbert_xyz2d_r(int cur_idx,\n                    int x_dst, int y_dst, int z_dst,\n                    int x, int y, int z,\n                    int ax, int ay, int az,\n                    int bx, int by, int bz,\n                    int cx, int cy, int cz) {\n  int w, h, d;\n  int w2, h2, d2;\n\n  int dax, day, daz,\n      dbx, dby, dbz,\n      dcx, dcy, dcz;\n\n  int ax2, ay2, az2,\n      bx2, by2, bz2,\n      cx2, cy2, cz2;\n\n  w = abs(ax + ay + az);\n  h = abs(bx + by + bz);\n  d = abs(cx + cy + cz);\n\n  dax = sgn(ax); day = sgn(ay); daz = sgn(az); // unit major direction (\"right\")\n  dbx = sgn(bx); dby = sgn(by); dbz = sgn(bz); // unit ortho direction (\"forward\")\n  dcx = sgn(cx); dcy = sgn(cy); dcz = sgn(cz); // unit ortho direction (\"up\")\n\n  // trivial row/column fills\n  if ((h == 1) && (d == 1)) {\n    return cur_idx + (dax*(x_dst - x)) + (day*(y_dst - y)) + (daz*(z_dst - z));\n  }\n\n  if ((w == 1) && (d == 1)) {\n    return cur_idx + (dbx*(x_dst - x)) + (dby*(y_dst - y)) + (dbz*(z_dst - z));\n  }\n\n  if ((w == 1) && (h == 1)) {\n    return cur_idx + (dcx*(x_dst - x)) + (dcy*(y_dst - y)) + (dcz*(z_dst - z));\n  }\n\n  ax2 = (int)floor((double)ax/2.0);\n  ay2 = (int)floor((double)ay/2.0);\n  az2 = (int)floor((double)az/2.0);\n\n  bx2 = (int)floor((double)bx/2.0);\n  by2 = (int)floor((double)by/2.0);\n  bz2 = (int)floor((double)bz/2.0);\n\n  cx2 = (int)floor((double)cx/2.0);\n  cy2 = (int)floor((double)cy/2.0);\n  cz2 = (int)floor((double)cz/2.0);\n\n  w2 = abs(ax2 + ay2 + az2);\n  h2 = abs(bx2 + by2 + bz2);\n  d2 = abs(cx2 + cy2 + cz2);\n\n  // prefer even steps\n  if ((w2 % 2) && (w > 2)) {\n    ax2 += dax;\n    ay2 += day;\n    az2 += daz;\n  }\n\n  if ((h2 % 2) && (h > 2)) {\n    bx2 += dbx;\n    by2 += dby;\n    bz2 += dbz;\n  }\n\n  if ((d2 % 2) && (d > 2)) {\n    cx2 += dcx;\n    cy2 += dcy;\n    cz2 += dcz;\n  }\n\n  // wide case, split in w only\n  if ((2*w > 3*h) && (2*w > 3*d)) {\n    if (in_bounds3(x_dst,y_dst,z_dst,\n                   x,y,z,\n                   ax2,ay2,az2,\n                   bx,by,bz,\n                   cx,cy,cz)) {\n      return gilbert_xyz2d_r(cur_idx,\n                             x_dst,y_dst,z_dst,\n                             x, y, z,\n                             ax2, ay2, az2,\n                             bx, by, bz,\n                             cx, cy, cz);\n    }\n    cur_idx += abs( (ax2 + ay2 + az2)*(bx + by + bz)*(cx + cy + cz) );\n\n    return gilbert_xyz2d_r(cur_idx,\n                           x_dst,y_dst,z_dst,\n                           x+ax2, y+ay2, z+az2,\n                           ax-ax2, ay-ay2, az-az2,\n                           bx, by, bz,\n                           cx, cy, cz);\n  }\n\n  // do not split in d\n  else if ((3*h) > (4*d)) {\n    if (in_bounds3(x_dst,y_dst,z_dst,\n                   x,y,z,\n                   bx2,by2,bz2,\n                   cx,cy,cz,\n                   ax2,ay2,az2)) {\n      return gilbert_xyz2d_r(cur_idx,\n                             x_dst,y_dst,z_dst,\n                             x, y, z,\n                             bx2, by2, bz2,\n                             cx, cy, cz,\n                             ax2, ay2, az2);\n    }\n    cur_idx += abs( (bx2 + by2 + bz2)*(cx + cy + cz)*(ax2 + ay2 + az2) );\n\n    if (in_bounds3(x_dst,y_dst,z_dst,\n                   x+bx2,y+by2,z+bz2,\n                   ax,ay,az,\n                   bx-bx2,by-by2,bz-bz2,\n                   cx,cy,cz)) {\n      return gilbert_xyz2d_r(cur_idx,\n                             x_dst,y_dst,z_dst,\n                             x+bx2, y+by2, z+bz2,\n                             ax, ay, az,\n                             bx-bx2, by-by2, bz-bz2,\n                             cx, cy, cz);\n    }\n    cur_idx += abs( (ax + ay + az)*((bx - bx2) + (by - by2) + (bz - bz2))*(cx + cy + cz) );\n\n    return gilbert_xyz2d_r(cur_idx,\n                           x_dst,y_dst,z_dst,\n                           x+(ax-dax)+(bx2-dbx),\n                           y+(ay-day)+(by2-dby),\n                           z+(az-daz)+(bz2-dbz),\n                           -bx2, -by2, -bz2,\n                           cx, cy, cz,\n                           -(ax-ax2), -(ay-ay2), -(az-az2));\n  }\n\n  // do not split in h\n  else if ((3*d) > (4*h)) {\n    if (in_bounds3(x_dst,y_dst,z_dst,\n                   x,y,z,\n                   cx2,cy2,cz2,\n                   ax2,ay2,az2, bx,by,bz)) {\n      return gilbert_xyz2d_r(cur_idx,\n                             x_dst,y_dst,z_dst,\n                             x, y, z,\n                             cx2, cy2, cz2,\n                             ax2, ay2, az2,\n                             bx, by, bz);\n    }\n    cur_idx += abs( (cx2 + cy2 + cz2)*(ax2 + ay2 + az2)*(bx + by + bz) );\n\n    if (in_bounds3(x_dst,y_dst,z_dst,\n                   x+cx2,y+cy2,z+cz2,\n                   ax,ay,az, bx,by,bz,\n                   cx-cx2,cy-cy2,cz-cz2)) {\n      return gilbert_xyz2d_r(cur_idx,\n                             x_dst,y_dst,z_dst,\n                             x+cx2, y+cy2, z+cz2,\n                             ax, ay, az,\n                             bx, by, bz,\n                             cx-cx2, cy-cy2, cz-cz2);\n    }\n    cur_idx += abs( (ax + ay + az)*(bx + by + bz)*((cx - cx2) + (cy - cy2) + (cz - cz2)) );\n\n    return gilbert_xyz2d_r(cur_idx,\n                           x_dst,y_dst,z_dst,\n                           x+(ax-dax)+(cx2-dcx),\n                           y+(ay-day)+(cy2-dcy),\n                           z+(az-daz)+(cz2-dcz),\n                           -cx2, -cy2, -cz2,\n                           -(ax-ax2), -(ay-ay2), -(az-az2),\n                           bx, by, bz);\n\n  }\n\n  // regular case, split in all w/h/d\n  if (in_bounds3(x_dst,y_dst,z_dst,\n                 x,y,z,\n                 bx2,by2,bz2,\n                 cx2,cy2,cz2,\n                 ax2,ay2,az2)) {\n    return gilbert_xyz2d_r(cur_idx,x_dst,y_dst,z_dst,\n                           x, y, z,\n                           bx2, by2, bz2,\n                           cx2, cy2, cz2,\n                           ax2, ay2, az2);\n  }\n  cur_idx += abs( (bx2 + by2 + bz2)*(cx2 + cy2 + cz2)*(ax2 + ay2 + az2) );\n\n  if (in_bounds3(x_dst,y_dst,z_dst,\n                 x+bx2, y+by2, z+bz2,\n                 cx, cy, cz,\n                 ax2, ay2, az2,\n                 bx-bx2, by-by2, bz-bz2)) {\n    return gilbert_xyz2d_r(cur_idx,\n                           x_dst,y_dst,z_dst,\n                           x+bx2, y+by2, z+bz2,\n                           cx, cy, cz,\n                           ax2, ay2, az2,\n                           bx-bx2, by-by2, bz-bz2);\n  }\n  cur_idx += abs( (cx + cy + cz)*(ax2 + ay2 + az2)*((bx - bx2) + (by - by2) + (bz - bz2)) );\n\n  if (in_bounds3(x_dst,y_dst,z_dst,\n                 x+(bx2-dbx)+(cx-dcx),\n                 y+(by2-dby)+(cy-dcy),\n                 z+(bz2-dbz)+(cz-dcz),\n                 ax, ay, az,\n                 -bx2, -by2, -bz2,\n                 -(cx-cx2), -(cy-cy2), -(cz-cz2))) {\n    return gilbert_xyz2d_r(cur_idx,\n                           x_dst,y_dst,z_dst,\n                           x+(bx2-dbx)+(cx-dcx),\n                           y+(by2-dby)+(cy-dcy),\n                           z+(bz2-dbz)+(cz-dcz),\n                           ax, ay, az,\n                           -bx2, -by2, -bz2,\n                           -(cx-cx2), -(cy-cy2), -(cz-cz2));\n  }\n  cur_idx += abs( (ax + ay + az)*(-bx2 - by2 - bz2)*(-(cx - cx2) - (cy - cy2) - (cz - cz2)) );\n\n  if (in_bounds3(x_dst,y_dst,z_dst,\n                 x+(ax-dax)+bx2+(cx-dcx),\n                 y+(ay-day)+by2+(cy-dcy),\n                 z+(az-daz)+bz2+(cz-dcz),\n                 -cx, -cy, -cz,\n                 -(ax-ax2), -(ay-ay2), -(az-az2),\n                 bx-bx2, by-by2, bz-bz2)) {\n    return gilbert_xyz2d_r(cur_idx,\n                           x_dst,y_dst,z_dst,\n                           x+(ax-dax)+bx2+(cx-dcx),\n                           y+(ay-day)+by2+(cy-dcy),\n                           z+(az-daz)+bz2+(cz-dcz),\n                           -cx, -cy, -cz,\n                           -(ax-ax2), -(ay-ay2), -(az-az2),\n                           bx-bx2, by-by2, bz-bz2);\n  }\n  cur_idx += abs( (-cx - cy - cz)*(-(ax - ax2) - (ay - ay2) - (az - az2))*((bx - bx2) + (by - by2) + (bz - bz2)) );\n\n  return gilbert_xyz2d_r(cur_idx,\n                         x_dst,y_dst,z_dst,\n                         x+(ax-dax)+(bx2-dbx),\n                         y+(ay-day)+(by2-dby),\n                         z+(az-daz)+(bz2-dbz),\n                         -bx2, -by2, -bz2,\n                         cx2, cy2, cz2,\n                         -(ax-ax2), -(ay-ay2), -(az-az2));\n}\n\n\n\n#define GILBERT_MAIN\n#ifdef GILBERT_MAIN\n\n#include <string.h>\n\nint main(int argc, char **argv) {\n  int w, h, d;\n  int x, y, z;\n  int idx;\n\n  char buf[1024];\n\n  w = 1;\n  h = 1;\n  d = 1;\n\n  if (argc < 4) {\n    printf(\"provide args\\n\");\n    printf(\"\\n\");\n    printf(\"usage:\\n\");\n    printf(\"\\n\");\n    printf(\"  gilbert <op> <width> <height> [depth]\\n\");\n    printf(\"\\n\");\n    printf(\"    op      - one of \\\"xy2d\\\",\\\"2dxy\\\",\\\"xyz2d\\\",\\\"d2xyz\\\"\\n\");\n    printf(\"    depth   - default to 1 for 3D Gilbert with no depth specified\\n\");\n    printf(\"\\n\");\n    exit(-1);\n  }\n\n  strncpy(buf, argv[1], 1023);\n  buf[1024]='\\0';\n\n  w = atoi(argv[2]);\n  h = atoi(argv[3]);\n  if (argc > 4) {\n    d = atoi(argv[4]);\n  }\n\n  if ((w <= 0) || (h <= 0) || (d <= 0)) {\n    exit(-1);\n  }\n\n  if (strncmp(\"xy2d\", buf, 1023) == 0) {\n\n    for (x = 0; x < w; x++) {\n      for (y = 0; y < h; y++) {\n        idx = gilbert_xy2d( x, y, w, h );\n        printf(\"%i %i %i\\n\", idx, x, y);\n      }\n    }\n\n  }\n  else if (strncmp(\"d2xy\", buf, 1023) == 0) {\n\n    for (idx = 0; idx < (w*h); idx++) {\n      gilbert_d2xy( &x, &y, idx, w, h );\n      printf(\"%i %i\\n\", x, y);\n    }\n\n  }\n  else if (strncmp(\"xyz2d\", buf, 1023) == 0) {\n\n    for (x = 0; x < w; x++) {\n      for (y = 0; y < h; y++) {\n        for (z = 0; z < d; z++) {\n          idx = gilbert_xyz2d( x,y,z, w,h,d );\n          printf(\"%i %i %i %i\\n\", idx, x, y, z);\n        }\n      }\n    }\n\n  }\n\n  else if (strncmp(\"d2xyz\", buf, 1023) == 0) {\n\n    for (idx = 0; idx < (w*h*d); idx++) {\n      gilbert_d2xyz( &x,&y,&z, idx, w,h,d );\n      printf(\"%i %i %i\\n\", x, y, z);\n    }\n\n  }\n\n  exit(0);\n}\n\n#endif\n"
  },
  {
    "path": "models/layers/gilbert/ports/gilbert.js",
    "content": "// SPDX-License-Identifier: BSD-2-Clause\n// Copyright (c) 2024 abetusk\n\n\"use strict\";\n\n\nvar gilbert = {\n  \"xy2d\": gilbert_xy2d,\n  \"d2xy\": gilbert_d2xy,\n\n  \"xyz2d\": gilbert_xyz2d,\n  \"d2xyz\": gilbert_d2xyz,\n};\n\nfunction sgn(x) {\n  if (x < 0) { return -1; }\n  if (x > 0) { return  1; }\n  return 0;\n}\n\nfunction in_bounds2(p, s, a, b) {\n  let d = { \"x\": a.x + b.x, \"y\": a.y + b.y };\n\n  if (d.x < 0) {\n    if ((p.x > s.x) || (p.x <= (s.x + d.x))) { return false; }\n  }\n  else if ((p.x < s.x) || (p.x >= (s.x + d.x))) { return false; }\n\n  if (d.y < 0) {\n    if ((p.y > s.y) || (p.y <= (s.y + d.y))) { return false; }\n  }\n  else if ((p.y < s.y) || (p.y >= (s.y + d.y))) { return false; }\n\n  return true;\n}\n\nfunction in_bounds3(p, s, a, b, c) {\n  let d = { \"x\": a.x + b.x + c.x, \"y\": a.y + b.y + c.y, \"z\": a.z + b.z + c.z };\n\n  if (d.x < 0) {\n    if ((p.x > s.x) || (p.x <= (s.x + d.x))) { return false; }\n  }\n  else if ((p.x < s.x) || (p.x >= (s.x + d.x))) { return false; }\n\n  if (d.y < 0) {\n    if ((p.y > s.y) || (p.y <= (s.y + d.y))) { return false; }\n  }\n  else if ((p.y < s.y) || (p.y >= (s.y + d.y))) { return false; }\n\n  if (d.z < 0) {\n    if ((p.z > s.z) || (p.z <= (s.z + d.z))) { return false; }\n  }\n  else if ((p.z < s.z) || (p.z >= (s.z + d.z))) { return false; }\n\n  return true;\n}\n\nfunction gilbert_xy2d(x,y,w,h) {\n  let _q = {\"x\": x, \"y\": y};\n  let _p = {\"x\": 0, \"y\": 0};\n  let _a = {\"x\": 0, \"y\": h};\n  let _b = {\"x\": w, \"y\": 0};\n\n  if (w >= h) {\n    _a.x = w; _a.y = 0;\n    _b.x = 0; _b.y = h;\n  }\n  return gilbert_xy2d_r(0, _q, _p, _a, _b);\n}\n\nfunction gilbert_d2xy(idx,w,h) {\n  let _p = {\"x\": 0, \"y\": 0};\n  let _a = {\"x\": 0, \"y\": h};\n  let _b = {\"x\": w, \"y\": 0};\n\n  if (w >= h) {\n    _a.x = w; _a.y = 0;\n    _b.x = 0; _b.y = h;\n  }\n  return gilbert_d2xy_r(idx,0,_p,_a,_b);\n}\n\nfunction gilbert_xyz2d(x,y,z,w,h,d) {\n  let _q = {\"x\": x, \"y\": y, \"z\": z};\n  let _p = {\"x\": 0, \"y\": 0, \"z\": 0};\n  let _a = {\"x\": w, \"y\": 0, \"z\": 0};\n  let _b = {\"x\": 0, \"y\": h, \"z\": 0};\n  let _c = {\"x\": 0, \"y\": 0, \"z\": d};\n\n  if ((w >= h) && (w >= d)) {\n    return gilbert_xyz2d_r(0, _q, _p, _a, _b, _c);\n  }\n  else if ((h >= w) && (h >= d)) {\n    return gilbert_xyz2d_r(0, _q, _p, _b, _a, _c);\n  }\n  return gilbert_xyz2d_r(0, _q, _p, _c, _a, _b);\n}\n\nfunction gilbert_d2xyz(idx,w,h,d) {\n  let _p = {\"x\": 0, \"y\": 0, \"z\": 0};\n  let _a = {\"x\": w, \"y\": 0, \"z\": 0};\n  let _b = {\"x\": 0, \"y\": h, \"z\": 0};\n  let _c = {\"x\": 0, \"y\": 0, \"z\": d};\n  if ((w >= h) && (w >= d)) {\n    return gilbert_d2xyz_r(idx, 0, _p, _a, _b, _c);\n  }\n  else if ((h >= w) && (h >= d)) {\n    return gilbert_d2xyz_r(idx, 0, _p, _b, _a, _c);\n  }\n  return gilbert_d2xyz_r(idx, 0, _p, _c, _a, _b);\n}\n\nfunction gilbert_d2xy_r( dst_idx,cur_idx, p, a, b) {\n  let _p = {}, _a = {}, _b = {};\n  let nxt_idx = -1;\n\n  let w = Math.abs( a.x + a.y );\n  let h = Math.abs( b.x + b.y );\n\n  let da = { \"x\": sgn(a.x), \"y\": sgn(a.y) };\n  let db = { \"x\": sgn(b.x), \"y\": sgn(b.y) };\n  let d = { \"x\": da.x + db.x, \"y\": da.y + db.y, \"i\": dst_idx - cur_idx };\n\n  if (h == 1) {\n    return { \"x\": p.x + da.x*d.i, \"y\": p.y + da.y*d.i }; }\n  if (w == 1) {\n    return {\"x\": p.x + db.x*d.i, \"y\": p.y + db.y*d.i }; }\n\n  let a2 = { \"x\": Math.floor(a.x/2), \"y\": Math.floor(a.y/2) };\n  let b2 = { \"x\": Math.floor(b.x/2), \"y\": Math.floor(b.y/2) };\n\n  let w2 = Math.abs(a2.x + a2.y);\n  let h2 = Math.abs(b2.x + b2.y);\n\n\n  if ((2*w) > (3*h)) {\n\n    // prefer even steps\n    if ((w2 % 2) && (w > 2)) {\n      a2.x += da.x;\n      a2.y += da.y;\n    }\n\n    nxt_idx = cur_idx + Math.abs((a2.x + a2.y)*(b.x + b.y));\n    if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n      return gilbert_d2xy_r(dst_idx,cur_idx, p, a2, b);\n    }\n    cur_idx = nxt_idx;\n\n    _p = { \"x\": p.x + a2.x, \"y\": p.y + a2.y };\n    _a = { \"x\": a.x - a2.x, \"y\": a.y - a2.y };\n\n    return gilbert_d2xy_r(dst_idx,cur_idx, _p, _a, b);\n  }\n\n  // prefer event steps\n  if ((h2 % 2) && (h > 2)) {\n    b2.x += db.x;\n    b2.y += db.y;\n  }\n\n  // standard case: one step up, on long horizontal, one step down\n  nxt_idx = cur_idx + Math.abs((b2.x + b2.y)*(a2.x + a2.y));\n  if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n    return gilbert_d2xy_r(dst_idx, cur_idx, p, b2, a2);\n  }\n  cur_idx = nxt_idx;\n\n  nxt_idx = cur_idx + Math.abs((a.x + a.y)*((b.x - b2.x) + (b.y - b2.y)));\n  if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n    _p = { \"x\": p.x + b2.x, \"y\": p.y + b2.y };\n    _b = { \"x\": b.x - b2.x, \"y\": b.y - b2.y };\n    return gilbert_d2xy_r(dst_idx, cur_idx, _p, a, _b);\n  }\n  cur_idx = nxt_idx;\n\n  _p = {\n    \"x\": p.x + (a.x - da.x) + (b2.x - db.x),\n    \"y\": p.y + (a.y - da.y) + (b2.y - db.y)\n  };\n  _a = { \"x\": -b2.x, \"y\": -b2.y };\n  _b = { \"x\": -(a.x - a2.x), \"y\": -(a.y - a2.y) };\n\n  return gilbert_d2xy_r(dst_idx, cur_idx, _p, _a, _b);\n}\n\nfunction gilbert_xy2d_r(idx, q, p, a, b) {\n  let _p = {}, _a = {}, _b = {};\n\n  let w = Math.abs(a.x + a.y);\n  let h = Math.abs(b.x + b.y);\n\n  let da = { \"x\": sgn(a.x), \"y\": sgn(a.y) };\n  let db = { \"x\": sgn(b.x), \"y\": sgn(b.y) };\n  let d = {\"x\": da.x + db.x, \"y\": da.y + db.y };\n\n  if (h == 1) {\n    return idx + (da.x*(q.x - p.x)) + (da.y*(q.y - p.y));\n  }\n  if (w == 1) {\n    return idx + (db.x*(q.x - p.x)) + (db.y*(q.y - p.y));\n  }\n\n  let a2 = { \"x\": Math.floor(a.x/2), \"y\": Math.floor(a.y/2) };\n  let b2 = { \"x\": Math.floor(b.x/2), \"y\": Math.floor(b.y/2) };\n\n  let w2 = Math.abs(a2.x + a2.y);\n  let h2 = Math.abs(b2.x + b2.y);\n\n  if ((2*w) > (3*h)) {\n    if ((w2 % 2) && (w > 2)) {\n      a2.x += da.x;\n      a2.y += da.y;\n    }\n\n    if (in_bounds2(q, p, a2, b)) {\n      return gilbert_xy2d_r(idx, q, p, a2, b);\n    }\n    idx += Math.abs((a2.x + a2.y)*(b.x + b.y));\n\n    _p = { \"x\": p.x + a2.x, \"y\": p.y + a2.y };\n    _a = { \"x\": a.x - a2.x, \"y\": a.y - a2.y };\n    return gilbert_xy2d_r(idx, q, _p, _a, b);\n  }\n\n  if ((h2 % 2) && (h > 2)) {\n    b2.x += db.x;\n    b2.y += db.y;\n  }\n\n  if (in_bounds2(q, p, b2, a2)) {\n    return gilbert_xy2d_r(idx, q, p, b2, a2);\n  }\n  idx += Math.abs((b2.x + b2.y)*(a2.x + a2.y));\n\n  _p = { \"x\": p.x + b2.x, \"y\": p.y + b2.y };\n  _b = { \"x\": b.x - b2.x, \"y\": b.y - b2.y };\n  if (in_bounds2(q, _p, a, _b)) {\n    return gilbert_xy2d_r(idx, q, _p, a, _b);\n  }\n  idx += Math.abs((a.x + a.y)*((b.x - b2.x) + (b.y - b2.y)));\n\n  _p = {\n    \"x\" : p.x + (a.x - da.x) + (b2.x - db.x),\n    \"y\" : p.y + (a.y - da.y) + (b2.y - db.y)\n  };\n  _a = { \"x\": -b2.x, \"y\": -b2.y };\n  _b = { \"x\": -(a.x - a2.x), \"y\": -(a.y - a2.y) };\n  return gilbert_xy2d_r(idx, q, _p, _a, _b);\n\n}\n\n\nfunction gilbert_xyz2d_r(cur_idx, q, p, a, b, c) {\n  let _p = {}, _a = {}, _b = {}, _c = {};\n\n  let w = Math.abs(a.x + a.y + a.z);\n  let h = Math.abs(b.x + b.y + b.z);\n  let d = Math.abs(c.x + c.y + c.z);\n\n  let da = { \"x\": sgn(a.x), \"y\": sgn(a.y), \"z\": sgn(a.z) };\n  let db = { \"x\": sgn(b.x), \"y\": sgn(b.y), \"z\": sgn(b.z) };\n  let dc = { \"x\": sgn(c.x), \"y\": sgn(c.y), \"z\": sgn(c.z) };\n\n  // trivial row/column fills\n  if ((h == 1) && (d == 1)) {\n    return cur_idx + (da.x*(q.x - p.x)) + (da.y*(q.y - p.y)) + (da.z*(q.z - p.z));\n  }\n  else if ((w == 1) && (d == 1)) {\n    return cur_idx + (db.x*(q.x - p.x)) + (db.y*(q.y - p.y)) + (db.z*(q.z - p.z));\n  }\n  else if ((w == 1) && (h == 1)) {\n    return cur_idx + (dc.x*(q.x - p.x)) + (dc.y*(q.y - p.y)) + (dc.z*(q.z - p.z));\n  }\n\n  let a2 = { \"x\": Math.floor(a.x/2), \"y\": Math.floor(a.y/2), \"z\": Math.floor(a.z/2) };\n  let b2 = { \"x\": Math.floor(b.x/2), \"y\": Math.floor(b.y/2), \"z\": Math.floor(b.z/2) };\n  let c2 = { \"x\": Math.floor(c.x/2), \"y\": Math.floor(c.y/2), \"z\": Math.floor(c.z/2) };\n\n  let w2 = Math.abs(a2.x + a2.y + a2.z);\n  let h2 = Math.abs(b2.x + b2.y + b2.z);\n  let d2 = Math.abs(c2.x + c2.y + c2.z);\n\n  // prefer even steps\n  if ((w2 % 2) && (w > 2)) {\n    a2.x += da.x;\n    a2.y += da.y;\n    a2.z += da.z;\n  }\n\n  if ((h2 % 2) && (h > 2)) {\n    b2.x += db.x;\n    b2.y += db.y;\n    b2.z += db.z;\n  }\n\n  if ((d2 % 2) && (d > 2)) {\n    c2.x += dc.x;\n    c2.y += dc.y;\n    c2.z += dc.z;\n  }\n\n  // wide case, split in w only\n  if ( ((2*w) > (3*h)) && ((2*w) > (3*d)) ) {\n    if (in_bounds3(q, p, a2, b, c)) {\n      return gilbert_xyz2d_r(cur_idx, q, p, a2, b, c);\n    }\n    cur_idx += Math.abs( (a2.x + a2.y + a2.z)*(b.x + b.y + b.z)*(c.x + c.y + c.z) );\n\n    _p = { \"x\": p.x + a2.x, \"y\": p.y + a2.y, \"z\": p.z + a2.z };\n    _a = { \"x\": a.x - a2.x, \"y\": a.y - a2.y, \"z\": a.z - a2.z };\n    return gilbert_xyz2d_r(cur_idx, q, _p, _a, b, c);\n  }\n\n  else if ((3*h) > (4*d)) {\n\n    if (in_bounds3(q, p, b2, c, a2)) {\n      return gilbert_xyz2d_r(cur_idx,q,p,b2,c,a2);\n    }\n    cur_idx += Math.abs( (b2.x + b2.y + b2.z)*(c.x + c.y + c.z)*(a2.x + a2.y + a2.z) );\n\n    _p = { \"x\": p.x + b2.x, \"y\": p.y + b2.y, \"z\": p.z + b2.z };\n    _b = { \"x\": b.x - b2.x, \"y\": b.y - b2.y, \"z\": b.z - b2.z };\n    if (in_bounds3(q, _p, a, _b, c)) {\n      return gilbert_xyz2d_r(cur_idx,q, _p, a, _b, c);\n    }\n    cur_idx += Math.abs( (a.x + a.y + a.z)*((b.x - b2.x) + (b.y - b2.y) + (b.z - b2.z))*(c.x + c.y + c.z) );\n\n    _p = {\n      \"x\": p.x + (a.x - da.x) + (b2.x - db.x),\n      \"y\": p.y + (a.y - da.y) + (b2.y - db.y),\n      \"z\": p.z + (a.z - da.z) + (b2.z - db.z)\n    };\n    _a = { \"x\": -b2.x, \"y\": -b2.y, \"z\": -b2.z };\n    _c = { \"x\": -(a.x - a2.x), \"y\": -(a.y - a2.y), \"z\": -(a.z - a2.z) };\n    return gilbert_xyz2d_r(cur_idx, q, _p, _a, c, _c);\n\n  }\n\n  else if ((3*d) > (4*h)) {\n\n    if (in_bounds3(q, p, c2, a2, b)) {\n      return gilbert_xyz2d_r(cur_idx,q,p,c2,a2,b);\n    }\n    cur_idx += Math.abs( (c2.x + c2.y + c2.z)*(a2.x + a2.y + a2.z)*(b.x + b.y + b.z) );\n\n    _p = { \"x\": p.x + c2.x, \"y\": p.y + c2.y, \"z\": p.z + c2.z };\n    _c = { \"x\": c.x - c2.x, \"y\": c.y - c2.y, \"z\": c.z - c2.z };\n    if (in_bounds3(q, _p, a, b, _c)) {\n      return gilbert_xyz2d_r(cur_idx, q, _p, a, b, _c);\n    }\n    cur_idx += Math.abs( (a.x + a.y + a.z)*(b.x + b.y + b.z)*((c.x - c2.x) + (c.y - c2.y) + (c.z - c2.z)) );\n\n    _p = {\n      \"x\": p.x + (a.x - da.x) + (c2.x - dc.x),\n      \"y\": p.y + (a.y - da.y) + (c2.y - dc.y),\n      \"z\": p.z + (a.z - da.z) + (c2.z - dc.z)\n    }\n    _a = { \"x\": -c2.x, \"y\": -c2.y, \"z\": -c2.z };\n    _b = { \"x\": -(a.x - a2.x), \"y\": -(a.y - a2.y), \"z\": -(a.z - a2.z) };\n    return gilbert_xyz2d_r(cur_idx, q, _p, _a, _b, b);\n\n  }\n\n  // regular case, split in all w/h/d\n  if (in_bounds3(q, p, b2, c2, a2)) {\n    return gilbert_xyz2d_r(cur_idx,q,p,b2,c2,a2);\n  }\n  cur_idx += Math.abs( (b2.x + b2.y + b2.z)*(c2.x + c2.y + c2.z)*(a2.x + a2.y + a2.z) );\n\n  _p = { \"x\": p.x + b2.x, \"y\": p.y + b2.y, \"z\": p.z + b2.z };\n  _c = { \"x\": b.x - b2.x, \"y\": b.y - b2.y, \"z\": b.z - b2.z };\n  if (in_bounds3(q, _p, c, a2, _c)) {\n    return gilbert_xyz2d_r(cur_idx, q, _p, c, a2, _c);\n  }\n  cur_idx += Math.abs( (c.x + c.y + c.z)*(a2.x + a2.y + a2.z)*((b.x - b2.x) + (b.y - b2.y) + (b.z - b2.z)) );\n\n  _p = {\n    \"x\" : p.x + (b2.x - db.x) + (c.x - dc.x),\n    \"y\" : p.y + (b2.y - db.y) + (c.y - dc.y),\n    \"z\" : p.z + (b2.z - db.z) + (c.z - dc.z)\n  };\n  _b = { \"x\": -b2.x, \"y\": -b2.y, \"z\": -b2.z };\n  _c = { \"x\": -(c.x - c2.x), \"y\": -(c.y - c2.y), \"z\": -(c.z - c2.z) };\n  if (in_bounds3(q, _p, a, _b, _c)) {\n    return gilbert_xyz2d_r(cur_idx, q, _p, a, _b, _c);\n  }\n  cur_idx += Math.abs( (a.x + a.y + a.z)*( -b2.x - b2.y - b2.z)*( -(c.x - c2.x) - (c.y - c2.y) - (c.z - c2.z)) );\n\n  _p = {\n    \"x\": p.x + (a.x - da.x) + b2.x + (c.x - dc.x),\n    \"y\": p.y + (a.y - da.y) + b2.y + (c.y - dc.y),\n    \"z\": p.z + (a.z - da.z) + b2.z + (c.z - dc.z)\n  };\n  _a = { \"x\": -c.x, \"y\": -c.y, \"z\": -c.z };\n  _b = { \"x\": -(a.x - a2.x), \"y\": -(a.y - a2.y), \"z\": -(a.z - a2.z) };\n  _c = { \"x\": b.x - b2.x, \"y\": b.y - b2.y, \"z\": b.z - b2.z };\n  if (in_bounds3(q, _p, _a, _b, _c)) {\n    return gilbert_xyz2d_r(cur_idx, q, _p, _a, _b, _c);\n  }\n  cur_idx += Math.abs( ( -c.x - c.y - c.z)*( -(a.x - a2.x) - (a.y - a2.y) - (a.z - a2.z))*((b.x - b2.x) + (b.y - b2.y) + (b.z - b2.z)) );\n\n  _p = {\n    \"x\": p.x + (a.x - da.x) + (b2.x - db.x),\n    \"y\": p.y + (a.y - da.y) + (b2.y - db.y),\n    \"z\": p.z + (a.z - da.z) + (b2.z - db.z)\n  };\n  _a = { \"x\": -b2.x, \"y\": -b2.y, \"z\": -b2.z };\n  _c = { \"x\": -(a.x - a2.x), \"y\": -(a.y - a2.y), \"z\": -(a.z - a2.z) };\n  return gilbert_xyz2d_r(cur_idx, q, _p, _a, c2, _c);\n\n}\n\nfunction gilbert_d2xyz_r(dst_idx, cur_idx, p, a, b, c) {\n  let _p = {}, _a = {}, _b = {}, _c = {};\n  let nxt_idx = -1;\n\n  let w = Math.abs(a.x + a.y + a.z);\n  let h = Math.abs(b.x + b.y + b.z);\n  let d = Math.abs(c.x + c.y + c.z);\n\n  let da = { \"x\": sgn(a.x), \"y\": sgn(a.y), \"z\": sgn(a.z) };\n  let db = { \"x\": sgn(b.x), \"y\": sgn(b.y), \"z\": sgn(b.z) };\n  let dc = { \"x\": sgn(c.x), \"y\": sgn(c.y), \"z\": sgn(c.z) };\n  let di = dst_idx - cur_idx;\n\n  // trivial row/column fills\n  if ((h == 1) && (d == 1)) {\n    return { \"x\": p.x + da.x*di, \"y\": p.y + da.y*di, \"z\": p.z + da.z*di };\n  }\n  else if ((w == 1) && (d == 1)) {\n    return { \"x\": p.x + db.x*di, \"y\": p.y + db.y*di, \"z\": p.z + db.z*di };\n  }\n  else if ((w == 1) && (h == 1)) {\n    return { \"x\": p.x + dc.x*di, \"y\": p.y + dc.y*di, \"z\": p.z + dc.z*di };\n  }\n\n  let a2 = { \"x\": Math.floor(a.x/2), \"y\": Math.floor(a.y/2), \"z\": Math.floor(a.z/2) };\n  let b2 = { \"x\": Math.floor(b.x/2), \"y\": Math.floor(b.y/2), \"z\": Math.floor(b.z/2) };\n  let c2 = { \"x\": Math.floor(c.x/2), \"y\": Math.floor(c.y/2), \"z\": Math.floor(c.z/2) };\n\n  let w2 = Math.abs(a2.x + a2.y + a2.z);\n  let h2 = Math.abs(b2.x + b2.y + b2.z);\n  let d2 = Math.abs(c2.x + c2.y + c2.z);\n\n  // prefer even steps\n  if ((w2 % 2) && (w > 2)) {\n    a2.x += da.x;\n    a2.y += da.y;\n    a2.z += da.z;\n  }\n\n  if ((h2 % 2) && (h > 2)) {\n    b2.x += db.x;\n    b2.y += db.y;\n    b2.z += db.z;\n  }\n\n  if ((d2 % 2) && (d > 2)) {\n    c2.x += dc.x;\n    c2.y += dc.y;\n    c2.z += dc.z;\n  }\n\n  // wide case, split in w only\n  if ( ((2*w) > (3*h)) && ((2*w) > (3*d)) ) {\n    nxt_idx = cur_idx + Math.abs( (a2.x + a2.y + a2.z)*(b.x + b.y + b.z)*(c.x + c.y + c.z) );\n    if ((cur_idx <= nxt_idx) && (dst_idx < nxt_idx)) {\n      return gilbert_d2xyz_r(dst_idx, cur_idx, p, a2, b, c);\n    }\n    cur_idx = nxt_idx;\n\n    _p = { \"x\": p.x + a2.x, \"y\": p.y + a2.y, \"z\": p.z + a2.z };\n    _a = { \"x\": a.x - a2.x, \"y\": a.y - a2.y, \"z\": a.z - a2.z };\n    return gilbert_d2xyz_r(dst_idx, cur_idx, _p, _a, b, c);\n  }\n\n  else if ((3*h) > (4*d)) {\n\n    nxt_idx = cur_idx + Math.abs( (b2.x + b2.y + b2.z)*(c.x + c.y + c.z)*(a2.x + a2.y + a2.z) );\n    if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n      return gilbert_d2xyz_r(dst_idx,cur_idx,p,b2,c,a2);\n    }\n    cur_idx = nxt_idx;\n\n    nxt_idx = cur_idx + Math.abs( (a.x + a.y + a.z)*((b.x - b2.x) + (b.y - b2.y) + (b.z - b2.z))*(c.x + c.y + c.z) );\n    _p = { \"x\": p.x + b2.x, \"y\": p.y + b2.y, \"z\": p.z + b2.z };\n    _b = { \"x\": b.x - b2.x, \"y\": b.y - b2.y, \"z\": b.z - b2.z };\n    if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n      return gilbert_d2xyz_r(dst_idx,cur_idx, _p, a, _b, c);\n    }\n    cur_idx = nxt_idx;\n\n    _p = {\n      \"x\": p.x + (a.x - da.x) + (b2.x - db.x),\n      \"y\": p.y + (a.y - da.y) + (b2.y - db.y),\n      \"z\": p.z + (a.z - da.z) + (b2.z - db.z)\n    };\n    _a = { \"x\": -b2.x, \"y\": -b2.y, \"z\": -b2.z };\n    _c = { \"x\": -(a.x - a2.x), \"y\": -(a.y - a2.y), \"z\": -(a.z - a2.z) };\n    return gilbert_d2xyz_r(dst_idx, cur_idx, _p, _a, c, _c);\n\n  }\n\n  else if ((3*d) > (4*h)) {\n\n    nxt_idx = cur_idx + Math.abs( (c2.x + c2.y + c2.z)*(a2.x + a2.y + a2.z)*(b.x + b.y + b.z) );\n    if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n      return gilbert_d2xyz_r(dst_idx,cur_idx,p,c2,a2,b);\n    }\n    cur_idx = nxt_idx;\n\n    nxt_idx = cur_idx + Math.abs( (a.x + a.y + a.z)*(b.x + b.y + b.z)*((c.x - c2.x) + (c.y - c2.y) + (c.z - c2.z)) );\n    _p = { \"x\": p.x + c2.x, \"y\": p.y + c2.y, \"z\": p.z + c2.z };\n    _c = { \"x\": c.x - c2.x, \"y\": c.y - c2.y, \"z\": c.z - c2.z };\n    if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n      return gilbert_d2xyz_r(dst_idx, cur_idx, _p, a, b, _c);\n    }\n    cur_idx = nxt_idx;\n\n    _p = {\n      \"x\": p.x + (a.x - da.x) + (c2.x - dc.x),\n      \"y\": p.y + (a.y - da.y) + (c2.y - dc.y),\n      \"z\": p.z + (a.z - da.z) + (c2.z - dc.z)\n    }\n    _a = { \"x\": -c2.x, \"y\": -c2.y, \"z\": -c2.z };\n    _b = { \"x\": -(a.x - a2.x), \"y\": -(a.y - a2.y), \"z\": -(a.z - a2.z) };\n    return gilbert_d2xyz_r(dst_idx, cur_idx, _p, _a, _b, b);\n\n  }\n\n  // regular case, split in all w/h/d\n  nxt_idx = cur_idx + Math.abs( (b2.x + b2.y + b2.z)*(c2.x + c2.y + c2.z)*(a2.x + a2.y + a2.z) );\n  if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n    return gilbert_d2xyz_r(dst_idx,cur_idx,p,b2,c2,a2);\n  }\n  cur_idx = nxt_idx;\n\n  nxt_idx = cur_idx + Math.abs( (c.x + c.y + c.z)*(a2.x + a2.y + a2.z)*((b.x - b2.x) + (b.y - b2.y) + (b.z - b2.z)) );\n  _p = { \"x\": p.x + b2.x, \"y\": p.y + b2.y, \"z\": p.z + b2.z };\n  _c = { \"x\": b.x - b2.x, \"y\": b.y - b2.y, \"z\": b.z - b2.z };\n  if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n    return gilbert_d2xyz_r(dst_idx, cur_idx, _p, c, a2, _c);\n  }\n  cur_idx = nxt_idx;\n\n  nxt_idx = cur_idx + Math.abs( (a.x + a.y + a.z)*( -b2.x - b2.y - b2.z)*( -(c.x - c2.x) - (c.y - c2.y) - (c.z - c2.z)) );\n  _p = {\n    \"x\" : p.x + (b2.x - db.x) + (c.x - dc.x),\n    \"y\" : p.y + (b2.y - db.y) + (c.y - dc.y),\n    \"z\" : p.z + (b2.z - db.z) + (c.z - dc.z)\n  };\n  _b = { \"x\": -b2.x, \"y\": -b2.y, \"z\": -b2.z };\n  _c = { \"x\": -(c.x - c2.x), \"y\": -(c.y - c2.y), \"z\": -(c.z - c2.z) };\n  if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n    return gilbert_d2xyz_r(dst_idx, cur_idx, _p, a, _b, _c);\n  }\n  cur_idx = nxt_idx;\n\n  nxt_idx = cur_idx + Math.abs( ( -c.x - c.y - c.z)*( -(a.x - a2.x) - (a.y - a2.y) - (a.z - a2.z))*((b.x - b2.x) + (b.y - b2.y) + (b.z - b2.z)) );\n  _p = {\n    \"x\": p.x + (a.x - da.x) + b2.x + (c.x - dc.x),\n    \"y\": p.y + (a.y - da.y) + b2.y + (c.y - dc.y),\n    \"z\": p.z + (a.z - da.z) + b2.z + (c.z - dc.z)\n  };\n  _a = { \"x\": -c.x, \"y\": -c.y, \"z\": -c.z };\n  _b = { \"x\": -(a.x - a2.x), \"y\": -(a.y - a2.y), \"z\": -(a.z - a2.z) };\n  _c = { \"x\": b.x - b2.x, \"y\": b.y - b2.y, \"z\": b.z - b2.z };\n  if ((cur_idx <= dst_idx) && (dst_idx < nxt_idx)) {\n    return gilbert_d2xyz_r(dst_idx, cur_idx, _p, _a, _b, _c);\n  }\n  cur_idx = nxt_idx;\n\n  _p = {\n    \"x\": p.x + (a.x - da.x) + (b2.x - db.x),\n    \"y\": p.y + (a.y - da.y) + (b2.y - db.y),\n    \"z\": p.z + (a.z - da.z) + (b2.z - db.z)\n  };\n  _a = { \"x\": -b2.x, \"y\": -b2.y, \"z\": -b2.z };\n  _c = { \"x\": -(a.x - a2.x), \"y\": -(a.y - a2.y), \"z\": -(a.z - a2.z) };\n  return gilbert_d2xyz_r(dst_idx, cur_idx, _p, _a, c2, _c);\n\n\n}\n\nif (typeof module !== \"undefined\") {\n\n  module.exports[\"d2xy\"] = gilbert_xy2d;\n  module.exports[\"xy2d\"] = gilbert_d2xy;\n\n  module.exports[\"d2xyz\"] = gilbert_xyz2d;\n  module.exports[\"xyz2d\"] = gilbert_d2xyz;\n\n  module.exports[\"main\"] = _main;\n\n  function _main(argv) {\n\n    if (argv.length < 4) {\n      console.log(\"provide args\");\n      process.exit(-1);\n    }\n\n    let op = argv[1];\n    let w = parseInt(argv[2]);\n    let h = parseInt(argv[3]);\n    let d = 1;\n    if (argv.length > 4) {\n      d = parseInt(argv[4]);\n    }\n\n    if (op == \"xy2d\") {\n      for (let x = 0; x < w; x++) {\n        for (let y = 0; y < h; y++) {\n          let idx = gilbert_xy2d(x,y,w,h);\n          console.log(idx, x, y);\n        }\n      }\n    }\n\n    else if (op == \"d2xy\") {\n      let n = w*h;\n      for (let idx = 0; idx < n; idx++) {\n        let xy = gilbert_d2xy(idx,w,h);\n        console.log(xy.x,xy.y);\n      }\n    }\n\n    else if (op == \"xyz2d\") {\n\n      for (let x = 0; x < w; x++) {\n        for (let y = 0; y < h; y++) {\n          for (let z = 0; z < d; z++) {\n            let idx = gilbert_xyz2d(x,y,z,w,h,d);\n            console.log(idx,x,y,z);\n          }\n        }\n      }\n\n    }\n\n    else if (op == \"d2xyz\") {\n      let n = w*h*d;\n      for (let idx = 0; idx < n; idx++) {\n        let xyz = gilbert_d2xyz(idx,w,h,d);\n        console.log(xyz.x,xyz.y,xyz.z);\n      }\n    }\n\n  }\n\n  //_main( process.argv.slice(1) );\n\n}\n\n\n"
  },
  {
    "path": "models/layers/gilbert/test.py",
    "content": ""
  },
  {
    "path": "models/layers/gilbert/tests/runtests.sh",
    "content": "#!/bin/bash\n#\n# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2018 abetusk\n\n\nln -f -s ../gilbert2d.py .\nln -f -s ../gilbert3d.py .\nln -f -s ../gilbert_d2xy.py .\nln -f -s ../gilbert_d2xyz.py .\nln -f -s ../gilbert_xy2d.py .\nln -f -s ../gilbert_xyz2d.py .\nln -f -s ../ports/gilbert.js .\nln -f -s ../ports/gilbert .\n\npushd ../ports\nmake\npopd\n\ngilbert_cmp2 () {\n  local x=$1\n  local y=$2\n\n  echo -n \"(python) xy2d[$x,$y]: \"\n  diff \\\n    <( ./gilbert2d.py $x $y 2> /dev/null ) \\\n    <( ./gilbert_xy2d.py $x $y | sort -n | cut -f2- -d' ' 2> /dev/null ) > /dev/null\n  if [[ $? != 0 ]] ; then echo \"FAIL\" ; else echo \"pass\" ; fi\n\n  echo -n \"(python) d2xy[$x,$y]: \"\n  diff \\\n    <( ./gilbert2d.py $x $y 2> /dev/null ) \\\n    <( ./gilbert_d2xy.py $x $y 2> /dev/null ) > /dev/null\n  if [[ $? != 0 ]] ; then echo \"FAIL\" ; else echo \"pass\" ; fi\n\n  ###\n\n  echo -n \"(js) xy2d[$x,$y]: \"\n  diff \\\n    <( ./gilbert2d.py $x $y 2> /dev/null ) \\\n    <( node -e 'require(\"./gilbert.js\").main([\"gilbert.js\",\"xy2d\",'$x','$y']);' | sort -n | cut -f2- -d' ' 2> /dev/null ) > /dev/null\n  if [[ $? != 0 ]] ; then echo \"FAIL\" ; else echo \"pass\" ; fi\n\n  echo -n \"(js) d2xy[$x,$y]: \"\n  diff \\\n    <( ./gilbert2d.py $x $y 2> /dev/null ) \\\n    <( node -e 'require(\"./gilbert.js\").main([\"gilbert.js\",\"d2xy\",'$x','$y']);' 2> /dev/null ) > /dev/null\n  if [[ $? != 0 ]] ; then echo \"FAIL\" ; else echo \"pass\" ; fi\n\n  ###\n\n  echo -n \"(c) xy2d[$x,$y]: \"\n  diff \\\n    <( ./gilbert2d.py $x $y 2> /dev/null ) \\\n    <( ./gilbert xy2d $x $y | sort -n | cut -f2- -d' ' 2> /dev/null ) > /dev/null\n  if [[ $? != 0 ]] ; then echo \"FAIL\" ; else echo \"pass\" ; fi\n\n  echo -n \"(c) d2xy[$x,$y]: \"\n  diff \\\n    <( ./gilbert2d.py $x $y 2> /dev/null ) \\\n    <( ./gilbert d2xy $x $y 2> /dev/null ) > /dev/null\n  if [[ $? != 0 ]] ; then echo \"FAIL\" ; else echo \"pass\" ; fi\n\n}\n\ngilbert_cmp3 () {\n  local x=$1\n  local y=$2\n  local z=$3\n\n  echo -n \"(python) xyz2d[$x,$y,$z]: \"\n#  diff \\\n#    <( ./gilbert3d.py $x $y $z 2> /dev/null ) \\\n#    <( ./gilbert3d.py --op xyz2d $x $y $z | sort -n | cut -f2- -d' ' 2> /dev/null ) > /dev/null\n  diff \\\n    <( ./gilbert3d.py $x $y $z 2> /dev/null ) \\\n    <( ./gilbert_xyz2d.py $x $y $z | sort -n | cut -f2- -d' ' 2> /dev/null ) > /dev/null\n  if [[ $? != 0 ]] ; then echo \"FAIL\" ; else echo \"pass\" ; fi\n\n  echo -n \"(python) d2xyz[$x,$y,$z]: \"\n#  diff \\\n#    <( ./gilbert3d.py $x $y $z 2> /dev/null ) \\\n#    <( ./gilbert3d.py --op d2xyz $x $y $z 2> /dev/null ) > /dev/null\n  diff \\\n    <( ./gilbert3d.py $x $y $z 2> /dev/null ) \\\n    <( ./gilbert_d2xyz.py $x $y $z 2> /dev/null ) > /dev/null\n  if [[ $? != 0 ]] ; then echo \"FAIL\" ; else echo \"pass\" ; fi\n\n  ###\n\n  echo -n \"(js) xyz2d[$x,$y,$z]: \"\n  diff \\\n    <( ./gilbert3d.py $x $y $z 2> /dev/null ) \\\n    <( node -e 'require(\"./gilbert.js\").main([\"gilbert.js\",\"xyz2d\",'$x','$y','$z']);' | sort -n | cut -f2- -d' ' 2> /dev/null ) > /dev/null\n  if [[ $? != 0 ]] ; then echo \"FAIL\" ; else echo \"pass\" ; fi\n\n  echo -n \"(js) d2xyz[$x,$y,$z]: \"\n  diff \\\n    <( ./gilbert3d.py $x $y $z 2> /dev/null ) \\\n    <( node -e 'require(\"./gilbert.js\").main([\"gilbert.js\",\"d2xyz\",'$x','$y','$z']);' 2> /dev/null ) > /dev/null\n  if [[ $? != 0 ]] ; then echo \"FAIL\" ; else echo \"pass\" ; fi\n\n  ###\n\n  echo -n \"(c) xyz2d[$x,$y,$z]: \"\n  diff \\\n    <( ./gilbert3d.py $x $y $z 2> /dev/null ) \\\n    <( ./gilbert xyz2d $x $y $z | sort -n | cut -f2- -d' ' 2> /dev/null ) > /dev/null\n  if [[ $? != 0 ]] ; then echo \"FAIL\" ; else echo \"pass\" ; fi\n\n  echo -n \"(c) d2xyz[$x,$y,$z]: \"\n  diff \\\n    <( ./gilbert3d.py $x $y $z 2> /dev/null ) \\\n    <( ./gilbert d2xyz $x $y $z 2> /dev/null ) > /dev/null\n  if [[ $? != 0 ]] ; then echo \"FAIL\" ; else echo \"pass\" ; fi\n\n}\n\nx=10 ; y=2\ngilbert_cmp2 $x $y\n\nx=2 ; y=10\ngilbert_cmp2 $x $y\n\nx=10 ; y=2 ; z=1\ngilbert_cmp3 $x $y $z\n\nx=2 ; y=10 ; z=1\ngilbert_cmp3 $x $y $z\n\n\nx=100 ; y=63\ngilbert_cmp2 $x $y\n\nx=63 ; y=100\ngilbert_cmp2 $x $y\n\nx=8 ; y=6 ; z=4\ngilbert_cmp3 $x $y $z\n\nx=40 ; y=30\ngilbert_cmp2 $x $y\n\nx=30 ; y=40\ngilbert_cmp2 $x $y\n\nx=40 ; y=30 ; z=20\ngilbert_cmp3 $x $y $z\n\nx=20 ; y=12 ; z=2\ngilbert_cmp3 $x $y $z\n\nx=15 ; y=12\ngilbert_cmp2 $x $y\n\nx=12 ; y=15\ngilbert_cmp2 $x $y\n\nx=7 ; y=6 ; z=4\ngilbert_cmp3 $x $y $z\n\n"
  },
  {
    "path": "models/layers/matching.py",
    "content": "import torch\nimport torch.nn.functional as F\n\n\n\n\ndef dice_loss(inputs, targets, num_boxes):\n    \"\"\"\n    Compute the DICE loss, similar to generalized IOU for masks\n    Args:\n        inputs: A float tensor of arbitrary shape.\n                The predictions for each example.\n        targets: A float tensor with the same shape as inputs. Stores the binary\n                 classification label for each element in inputs\n                (0 for the negative class and 1 for the positive class).\n    \"\"\"\n    inputs = inputs.sigmoid()\n    inputs = inputs.flatten(1)\n    numerator = 2 * (inputs * targets).sum(1)\n    denominator = inputs.sum(-1) + targets.sum(-1)\n    loss = 1 - (numerator + 1) / (denominator + 1)\n    return loss.sum() / num_boxes\n\ndef ber_loss(inputs, targets, num_boxes):\n    inputs = inputs.sigmoid()\n    inputs = inputs.flatten(1)\n    numerator = (inputs * targets).sum(1) # tp\n    denominator = targets.sum(-1)\n    loss = 1 - (numerator + 1) / (denominator + 1)\n    return loss.sum() / num_boxes\n\ndef ce_mask_loss(inputs, targets, num_boxes,\n                 weight=None):\n    \"\"\"\n    Loss used in RetinaNet for dense detection: https://arxiv.org/abs/1708.02002.\n    Args:\n        inputs: \n            b=n_sigma thw\n        targets: b=n_sigma thw\n            (0 for the negative class and 1 for the positive class).\n    Returns:\n        Loss tensor\n    \"\"\"\n    # b hw\n    ce_loss = F.binary_cross_entropy_with_logits(inputs, targets, reduction=\"none\")\n    # weight: b hw\n    if weight is not None:\n        ce_loss = ce_loss * weight\n    return ce_loss.mean(1).sum() / num_boxes\n\ndef sigmoid_focal_loss(inputs, targets, num_boxes, alpha: float = 0.25, gamma: float = 2):\n    \"\"\"\n    Loss used in RetinaNet for dense detection: https://arxiv.org/abs/1708.02002.\n    Args:\n        inputs: A float tensor of arbitrary shape.\n                The predictions for each example.\n        targets: A float tensor with the same shape as inputs. Stores the binary\n                 classification label for each element in inputs\n                (0 for the negative class and 1 for the positive class).\n        alpha: (optional) Weighting factor in range (0,1) to balance\n                positive vs negative examples. Default = -1 (no weighting).\n        gamma: Exponent of the modulating factor (1 - p_t) to\n               balance easy vs hard examples.\n    Returns:\n        Loss tensor\n    \"\"\"\n    prob = inputs.sigmoid()\n    ce_loss = F.binary_cross_entropy_with_logits(inputs, targets, reduction=\"none\")\n    p_t = prob * targets + (1 - prob) * (1 - targets)\n    loss = ce_loss * ((1 - p_t) ** gamma)\n\n    if alpha >= 0:\n        alpha_t = alpha * targets + (1 - alpha) * (1 - targets)\n        loss = alpha_t * loss\n\n    return loss.mean(1).sum() / num_boxes\n\ndef batch_sigmoid_focal_loss(inputs, targets, alpha: float = 0.25, gamma: float = 2):\n    N, M = len(inputs), len(targets)\n    inputs = inputs.flatten(1).unsqueeze(1).expand(-1, M, -1) # [N, M, THW]\n    targets = targets.flatten(1).unsqueeze(0).expand(N, -1, -1) # [N, M, THW]\n\n    prob = inputs.sigmoid()\n    ce_loss = F.binary_cross_entropy_with_logits(inputs, targets, reduction=\"none\")\n    p_t = prob * targets + (1 - prob) * (1 - targets)\n    coef = ce_loss * ((1 - p_t) ** gamma)\n\n    if alpha >= 0:\n        alpha_t = alpha * targets + (1 - alpha) * (1 - targets)\n        coef = alpha_t * coef\n\n    return coef.mean(2) # [N, M]\n\ndef batch_dice_loss(inputs: torch.Tensor, targets: torch.Tensor):\n    \"\"\"\n    Compute the DICE loss, similar to generalized IOU for masks\n    Args:\n        inputs: A float tensor of arbitrary shape.\n                The predictions for each example.\n        targets: A float tensor with the same shape as inputs. Stores the binary\n                 classification label for each element in inputs\n                (0 for the negative class and 1 for the positive class).\n    \"\"\"\n    inputs = inputs.sigmoid()\n    inputs = inputs.flatten(1)\n    numerator = 2 * torch.einsum(\"nc,mc->nm\", inputs, targets)\n    denominator = inputs.sum(-1)[:, None] + targets.sum(-1)[None, :]\n    loss = 1 - (numerator + 1) / (denominator + 1)\n    return loss\n\ndef batch_sigmoid_ce_loss(inputs: torch.Tensor, targets: torch.Tensor,):\n    \"\"\"\n    Args:\n        inputs: A float tensor of arbitrary shape.\n                The predictions for each example.\n        targets: A float tensor with the same shape as inputs. Stores the binary\n                 classification label for each element in inputs\n                (0 for the negative class and 1 for the positive class).\n    Returns:\n        Loss tensor\n    \"\"\"\n    hw = inputs.shape[1]\n\n    pos = F.binary_cross_entropy_with_logits(\n        inputs, torch.ones_like(inputs), reduction=\"none\"\n    )\n    neg = F.binary_cross_entropy_with_logits(\n        inputs, torch.zeros_like(inputs), reduction=\"none\"\n    )\n\n    loss = torch.einsum(\"nc,mc->nm\", pos, targets) + torch.einsum(\n        \"nc,mc->nm\", neg, (1 - targets)\n    )\n\n    return loss / hw\n\ndef get_src_permutation_idx(indices):\n    # permute predictions following indices\n    batch_idx = torch.cat([torch.full_like(src, i) for i, (src, _) in enumerate(indices)])\n    src_idx = torch.cat([src for (src, _) in indices])\n    return batch_idx, src_idx\n\ndef get_tgt_permutation_idx(indices):\n    # permute targets following indices\n    batch_idx = torch.cat([torch.full_like(tgt, i) for i, (_, tgt) in enumerate(indices)])\n    tgt_idx = torch.cat([tgt for (_, tgt) in indices])\n    return batch_idx, tgt_idx\n"
  },
  {
    "path": "models/layers/position_encoding.py",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n\"\"\"\nVarious positional encodings for the transformer.\n\"\"\"\nimport math\nimport torch\nfrom torch import nn\n\nfrom utils.misc import NestedTensor\n\nclass PositionEmbeddingSine(nn.Module):\n    \"\"\"\n    This is a more standard version of the position embedding, very similar to the one\n    used by the Attention is all you need paper, generalized to work on images.\n    \"\"\"\n    def __init__(self, num_pos_feats=64, temperature=10000, normalize=False, scale=None):\n        super().__init__()\n        self.num_pos_feats = num_pos_feats\n        self.temperature = temperature\n        self.normalize = normalize\n        if scale is not None and normalize is False:\n            raise ValueError(\"normalize should be True if scale is passed\")\n        if scale is None:\n            scale = 2 * math.pi\n        self.scale = scale\n\n    def forward(self, tensor_list: NestedTensor):\n        x = tensor_list.tensors\n        mask = tensor_list.mask\n        assert mask is not None\n        not_mask = ~mask\n        y_embed = not_mask.cumsum(1, dtype=torch.float32)\n        x_embed = not_mask.cumsum(2, dtype=torch.float32)\n        if self.normalize:\n            eps = 1e-6\n            y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale\n            x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale\n\n        dim_t = torch.arange(self.num_pos_feats, dtype=torch.float32, device=x.device)\n        dim_t = self.temperature ** (2 * (dim_t // 2) / self.num_pos_feats)\n\n        pos_x = x_embed[:, :, :, None] / dim_t\n        pos_y = y_embed[:, :, :, None] / dim_t\n        pos_x = torch.stack((pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4).flatten(3)\n        pos_y = torch.stack((pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4).flatten(3)\n        pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2)\n        return pos\n\n\nclass PositionEmbeddingLearned(nn.Module):\n    \"\"\"\n    Absolute pos embedding, learned.\n    \"\"\"\n    def __init__(self, num_pos_feats=256):\n        super().__init__()\n        self.row_embed = nn.Embedding(50, num_pos_feats)\n        self.col_embed = nn.Embedding(50, num_pos_feats)\n        self.reset_parameters()\n\n    def reset_parameters(self):\n        nn.init.uniform_(self.row_embed.weight)\n        nn.init.uniform_(self.col_embed.weight)\n\n    def forward(self, tensor_list: NestedTensor):\n        x = tensor_list.tensors\n        h, w = x.shape[-2:]\n        i = torch.arange(w, device=x.device)\n        j = torch.arange(h, device=x.device)\n        x_emb = self.col_embed(i)\n        y_emb = self.row_embed(j)\n        pos = torch.cat([\n            x_emb.unsqueeze(0).repeat(h, 1, 1),\n            y_emb.unsqueeze(1).repeat(1, w, 1),\n        ], dim=-1).permute(2, 0, 1).unsqueeze(0).repeat(x.shape[0], 1, 1, 1)\n        return pos\n\n# dimension == 1\nclass PositionEmbeddingSine1D(nn.Module):\n    \"\"\"\n    This is a more standard version of the position embedding, very similar to the one\n    used by the Attention is all you need paper, generalized to work on images.\n    \"\"\"\n    def __init__(self, temperature=10000, normalize=True, scale=None):\n        super().__init__()\n        self.temperature = temperature\n        self.normalize = normalize\n        if scale is not None and normalize is False:\n            raise ValueError(\"normalize should be True if scale is passed\")\n        if scale is None:\n            scale = 2 * math.pi\n        self.scale = scale\n\n    def forward(self, mask, hidden_dim):\n        device = mask.device\n        num_pos_feats = hidden_dim\n        assert mask is not None\n        not_mask = ~mask\n        x_embed = not_mask.cumsum(1, dtype=torch.float32)  # [B, T]\n        if self.normalize:\n            eps = 1e-6\n            x_embed = x_embed / (x_embed[:, -1:] + eps) * self.scale\n\n        dim_t = torch.arange(num_pos_feats, dtype=torch.float32, device=device)\n        dim_t = self.temperature ** (2 * (dim_t // 2) / num_pos_feats)\n\n        pos_x = x_embed[:, :, None] / dim_t  # [B, T, C]\n        # n,c,t\n        pos_x = torch.stack((pos_x[:, :, 0::2].sin(), pos_x[:, :, 1::2].cos()), dim=3).flatten(2)\n        pos = pos_x.permute(0, 2, 1)    # [B, C, T]\n        return pos\n\n# dimension == 3\nclass PositionEmbeddingSine3D(nn.Module):\n    \"\"\"\n    This is a more standard version of the position embedding, very similar to the one\n    used by the Attention is all you need paper, generalized to work on images.\n    \"\"\"\n\n    def __init__(self, num_pos_feats=64, temperature=10000, normalize=False, scale=None):\n        super().__init__()\n        self.num_pos_feats = num_pos_feats\n        self.temperature = temperature\n        self.normalize = normalize\n        if scale is not None and normalize is False:\n            raise ValueError(\"normalize should be True if scale is passed\")\n        if scale is None:\n            scale = 2 * math.pi\n        self.scale = scale\n\n    def forward(self, x, mask=None):\n        # b, t, c, h, w\n        assert x.dim() == 5, f\"{x.shape} should be a 5-dimensional Tensor, got {x.dim()}-dimensional Tensor instead\"\n        if mask is None:\n            mask = torch.zeros((x.size(0), x.size(1), x.size(3), x.size(4)), device=x.device, dtype=torch.bool)\n        not_mask = ~mask\n        z_embed = not_mask.cumsum(1, dtype=torch.float32)\n        y_embed = not_mask.cumsum(2, dtype=torch.float32)\n        x_embed = not_mask.cumsum(3, dtype=torch.float32)\n        if self.normalize:\n            eps = 1e-6\n            z_embed = z_embed / (z_embed[:, -1:, :, :] + eps) * self.scale\n            y_embed = y_embed / (y_embed[:, :, -1:, :] + eps) * self.scale\n            x_embed = x_embed / (x_embed[:, :, :, -1:] + eps) * self.scale\n\n        dim_t = torch.arange(self.num_pos_feats, dtype=torch.float32, device=x.device)\n        dim_t = self.temperature ** (2 * (dim_t // 2) / self.num_pos_feats)\n\n        dim_t_z = torch.arange((self.num_pos_feats * 2), dtype=torch.float32, device=x.device)\n        dim_t_z = self.temperature ** (2 * (dim_t_z // 2) / (self.num_pos_feats * 2))\n\n        pos_x = x_embed[:, :, :, :, None] / dim_t\n        pos_y = y_embed[:, :, :, :, None] / dim_t\n        pos_z = z_embed[:, :, :, :, None] / dim_t_z\n        pos_x = torch.stack((pos_x[:, :, :, :, 0::2].sin(), pos_x[:, :, :, :, 1::2].cos()), dim=5).flatten(4)\n        pos_y = torch.stack((pos_y[:, :, :, :, 0::2].sin(), pos_y[:, :, :, :, 1::2].cos()), dim=5).flatten(4)\n        pos_z = torch.stack((pos_z[:, :, :, :, 0::2].sin(), pos_z[:, :, :, :, 1::2].cos()), dim=5).flatten(4)\n        pos = (torch.cat((pos_y, pos_x), dim=4) + pos_z).permute(0, 1, 4, 2, 3)  # b, t, c, h, w\n        return pos\n\n\nclass PositionEmbeddingSine2D(nn.Module):\n    \"\"\"\n    This is a more standard version of the position embedding, very similar to the one\n    used by the Attention is all you need paper, generalized to work on images.\n    \"\"\"\n    def __init__(self, temperature=10000, normalize=True, scale=None):\n        super().__init__()\n        self.temperature = temperature\n        self.normalize = normalize\n        if scale is not None and normalize is False:\n            raise ValueError(\"normalize should be True if scale is passed\")\n        if scale is None:\n            scale = 2 * math.pi\n        self.scale = scale\n\n    def forward(self, mask, hidden_dim: int):\n        \"\"\"\n        @param mask: a tensor of shape [B, H, W]\n        @param hidden_dim: int\n        @return:\n        position embedding of the same shape [B, hidden_dim, H, W]\n        \"\"\"\n        num_pos_feats = hidden_dim // 2\n\n        not_mask = ~mask\n        y_embed = not_mask.cumsum(1, dtype=torch.float32)\n        x_embed = not_mask.cumsum(2, dtype=torch.float32)\n        if self.normalize:\n            eps = 1e-6\n            y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale\n            x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale\n\n        dim_t = torch.arange(num_pos_feats, dtype=torch.float32, device=mask.device)\n        dim_t = self.temperature ** (2 * (dim_t // 2) / num_pos_feats)\n\n        pos_x = x_embed[:, :, :, None] / dim_t\n        pos_y = y_embed[:, :, :, None] / dim_t\n        pos_x = torch.stack((pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4).flatten(3)\n        pos_y = torch.stack((pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4).flatten(3)\n        pos = torch.cat((pos_y, pos_x), dim=3).permute(0,3,1,2)\n        return pos # b c h w\n\nclass PositionEmbeddingLearned1D(nn.Module):\n    \"\"\"\n    Absolute pos embedding, learned.\n    \"\"\"\n    def __init__(self, num_pos_feats=256):\n        super().__init__()\n        self.row_embed = nn.Embedding(50, num_pos_feats)\n        self.reset_parameters()\n\n    def reset_parameters(self):\n        nn.init.uniform_(self.row_embed.weight)\n\n    def forward(self, tensor_list: NestedTensor):\n        \"\"\"\n        Input:\n            - tensor_list: \n                NT(b s d, b s)\n        \"\"\"\n        x = tensor_list.tensors\n        sequence_length = x.shape[-2]\n        i = torch.arange(sequence_length, device=x.device)\n        x_emb = self.row_embed(i) # s d\n        pos = x_emb.unsqueeze(0).repeat(x.shape[0], 1, 1)\n        return pos\n\ndef build_position_encoding(hidden_dim=None, position_embedding_name='2d'):\n    if position_embedding_name == 'original_2d':\n        # TODO find a better way of exposing other arguments\n        N_steps = hidden_dim // 2\n        return PositionEmbeddingSine(N_steps, normalize=True)\n    elif position_embedding_name == 'learned_2d':\n        N_steps = hidden_dim // 2\n        return PositionEmbeddingLearned(N_steps)\n    elif position_embedding_name == 'learned_1d':\n        assert hidden_dim is not None\n        return PositionEmbeddingLearned1D(hidden_dim)\n    elif position_embedding_name == '1d':\n        return PositionEmbeddingSine1D(normalize=True)\n    elif position_embedding_name == '2d':\n        return PositionEmbeddingSine2D(normalize=True)\n    elif position_embedding_name == '3d':\n        N_steps = hidden_dim // 2\n        return PositionEmbeddingSine3D(N_steps, normalize=True)\n    else:\n        raise ValueError(f\"not supported {position_embedding_name}\")\n\n"
  },
  {
    "path": "models/layers/utils.py",
    "content": "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport copy\n\ndef zero_module(module):\n    \"\"\"\n    Zero out the parameters of a module and return it.\n    \"\"\"\n    for p in module.parameters():\n        p.detach().zero_()\n    return module\n\ndef _get_clones(module, N):\n    return nn.ModuleList([copy.deepcopy(module) for i in range(N)])\n\ndef _get_activation_fn(activation):\n    \"\"\"Return an activation function given a string\"\"\"\n    if activation == \"relu\":\n        return F.relu\n    if activation == \"gelu\":\n        return F.gelu\n    if activation == \"glu\":\n        return F.glu\n    if activation == None:\n        return None\n    raise RuntimeError(F\"activation should be relu/gelu, not {activation}.\")\n\ndef _get_activation_layer(activation):\n    if activation == \"relu\":\n        return nn.ReLU()\n    if activation == \"gelu\":\n        return nn.GELU()\n    if activation == \"glu\":\n        return nn.GLU()\n    if activation == 'none':\n        return nn.Identity()\n    raise RuntimeError(F\"activation should be relu/gelu, not {activation}.\")\n\ndef pad_1d_feats(feat_list):\n    # list[ni c] -> b nmax c\n    feat_len = [len(feat) for feat in feat_list]\n    n_max = max(feat_len) \n    batch_size = len(feat_list)\n    pad_mask = torch.ones([batch_size, n_max], dtype=torch.bool, device=feat_list[0].device)\n    for i in range(batch_size):\n        feat_list[i] = F.pad(feat_list[i].clone(), pad=[0, 0, 0, n_max-feat_len[i]])\n        pad_mask[i, :feat_len[i]] = False\n    feat_list = torch.stack(feat_list, dim=0) # b nmax c\n    return feat_list, pad_mask\n\n\n    \n"
  },
  {
    "path": "models/modality_input_mappers/__init__.py",
    "content": "from .hilbert_curve import (\n    HilbertCurve_FrameQuery\n)"
  },
  {
    "path": "models/modality_input_mappers/hilbert_curve.py",
    "content": "\nfrom models.registry import MODELITY_INPUT_MAPPER_REGISTRY\nimport logging\nimport torch\nfrom models.layers.gilbert.gilbert2d import gilbert2d_widthBigger\n@MODELITY_INPUT_MAPPER_REGISTRY.register()\nclass HilbertCurve_FrameQuery:\n    def __init__(self,\n                 configs,\n                 ) -> None:\n        \n        self.frame_query_number = configs['frame_query_number']   \n              \n    def mapper(self, video):\n        return {\n            'haosen': None,\n        }\n        \n    def collate(self, list_of_haosen, batch_videos):\n        batch_size, T = batch_videos.shape[:2] \n        batch_size, T, _, H, W = batch_videos.shape\n        hilbert_curve = list(gilbert2d_widthBigger(width=self.frame_query_number, height=T)) # list[(x(width), y(height))]\n        hilbert_curve = torch.tensor(hilbert_curve).long()\n        hilbert_curve = hilbert_curve[:, 1] * self.frame_query_number + hilbert_curve[:, 0]\n        \n        return { \n            'hilbert_curve': hilbert_curve,\n        }\n\n\n        \n\n     "
  },
  {
    "path": "models/optimization/optimizer.py",
    "content": "\nfrom detectron2.solver.build import maybe_add_gradient_clipping\nfrom collections import OrderedDict\nfrom typing import Any, Dict, List, Set, Union, Iterable, Callable, Type, Optional\nimport copy\nimport itertools\nimport torch\nfrom enum import Enum\n_GradientClipperInput = Union[torch.Tensor, Iterable[torch.Tensor]]\n_GradientClipper = Callable[[_GradientClipperInput], None]\n\n\nclass GradientClipType(Enum):\n    VALUE = \"value\"\n    NORM = \"norm\"\n\n\ndef maybe_add_full_model_gradient_clipping(optim, configs):\n    # detectron2 doesn't have full model gradient clipping now\n    clip_norm_val = configs['optim']['clip_gradients']['clip_value']\n    enable = (\n        configs['optim']['clip_gradients']['enabled']\n        and configs['optim']['clip_gradients']['clip_type'] == \"full_model\"\n        and configs['optim']['clip_gradients']['clip_value'] > 0.0\n    )\n\n    class FullModelGradientClippingOptimizer(optim):\n        def step(self, closure=None):\n            all_params = itertools.chain(*[x[\"params\"] for x in self.param_groups])\n            torch.nn.utils.clip_grad_norm_(all_params, clip_norm_val)\n            super().step(closure=closure)\n\n    return FullModelGradientClippingOptimizer if enable else optim\n\n\ndef _create_gradient_clipper(cfg) -> _GradientClipper:\n    \"\"\"\n    Creates gradient clipping closure to clip by value or by norm,\n    according to the provided config.\n    \"\"\"\n    cfg = copy.deepcopy(cfg)\n\n    def clip_grad_norm(p: _GradientClipperInput):\n        torch.nn.utils.clip_grad_norm_(p, cfg['clip_value'], cfg['norm_type'])\n\n    def clip_grad_value(p: _GradientClipperInput):\n        torch.nn.utils.clip_grad_value_(p, cfg['clip_value'])\n\n    _GRADIENT_CLIP_TYPE_TO_CLIPPER = {\n        GradientClipType.VALUE: clip_grad_value,\n        GradientClipType.NORM: clip_grad_norm,\n    }\n    return _GRADIENT_CLIP_TYPE_TO_CLIPPER[GradientClipType(cfg['clip_type'])]\n\ndef _generate_optimizer_class_with_gradient_clipping(\n    optimizer: Type[torch.optim.Optimizer],\n    *,\n    per_param_clipper: Optional[_GradientClipper] = None,\n    global_clipper: Optional[_GradientClipper] = None,\n) -> Type[torch.optim.Optimizer]:\n    \"\"\"\n    Dynamically creates a new type that inherits the type of a given instance\n    and overrides the `step` method to add gradient clipping\n    \"\"\"\n    assert (\n        per_param_clipper is None or global_clipper is None\n    ), \"Not allowed to use both per-parameter clipping and global clipping\"\n\n    def optimizer_wgc_step(self, closure=None):\n        if per_param_clipper is not None:\n            for group in self.param_groups:\n                for p in group[\"params\"]:\n                    per_param_clipper(p)\n        else:\n            # global clipper for future use with detr\n            # (https://github.com/facebookresearch/detr/pull/287)\n            all_params = itertools.chain(*[g[\"params\"] for g in self.param_groups])\n            global_clipper(all_params)\n        super(type(self), self).step(closure)\n\n    OptimizerWithGradientClip = type(\n        optimizer.__name__ + \"WithGradientClip\",\n        (optimizer,),\n        {\"step\": optimizer_wgc_step},\n    )\n    return OptimizerWithGradientClip\n\ndef maybe_add_gradient_clipping(\n    configs: dict, optimizer: torch.optim.Optimizer):\n    \"\"\"\n    If gradient clipping is enabled through config options, wraps the existing\n    optimizer type to become a new dynamically created class OptimizerWithGradientClip\n    that inherits the given optimizer and overrides the `step` method to\n    include gradient clipping.\n\n    Args:\n        cfg: CfgNode, configuration options\n        optimizer: type. A subclass of torch.optim.Optimizer\n\n    Return:\n        type: either the input `optimizer` (if gradient clipping is disabled), or\n            a subclass of it with gradient clipping included in the `step` method.\n    \"\"\"\n    if not configs['optim']['clip_gradients']['enabled']:\n        return optimizer\n    if isinstance(optimizer, torch.optim.Optimizer):\n        optimizer_type = type(optimizer)\n    else:\n        assert issubclass(optimizer, torch.optim.Optimizer), optimizer\n        optimizer_type = optimizer\n\n    grad_clipper = _create_gradient_clipper(configs['optim']['clip_gradients'])\n    OptimizerWithGradientClip = _generate_optimizer_class_with_gradient_clipping(\n        optimizer_type, per_param_clipper=grad_clipper\n    )\n    if isinstance(optimizer, torch.optim.Optimizer):\n        optimizer.__class__ = OptimizerWithGradientClip  # a bit hacky, not recommended\n        return optimizer\n    else:\n        return OptimizerWithGradientClip\n\ndef get_optimizer(params, configs):\n    optimizer_type = configs['optim']['name']\n    base_lr = configs['optim']['base_lr']\n    weight_decay = configs['optim']['weight_decay'] if 'weight_decay' in configs['optim'] else configs['optim']['base_wd']\n    if optimizer_type == \"AdamW\":\n        optimizer = maybe_add_full_model_gradient_clipping(torch.optim.AdamW, configs)(\n            params, base_lr, weight_decay=weight_decay,\n        )\n    else:\n        raise NotImplementedError(f\"no optimizer type {optimizer_type}\")\n    \n    if configs['optim']['clip_gradients']['clip_type'] != \"full_model\":\n        optimizer = maybe_add_gradient_clipping(configs, optimizer)\n    \n    return optimizer\n\n\n"
  },
  {
    "path": "models/optimization/scheduler.py",
    "content": "import torch\nfrom functools import partial\nimport logging\nimport numpy as np\n\n\ndef build_scheduler(configs, optimizer):\n    name = configs['optim']['scheduler']['name']\n    scheduler_configs = configs['optim']['scheduler']\n\n    if name == 'multistep_lr':\n        scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer,\n                                                        milestones=scheduler_configs['milestones'],\n                                                        gamma=scheduler_configs['gamma'],\n                                                        verbose=scheduler_configs['verbose'],)\n        return scheduler\n    else:\n        raise ValueError()\n\n\n\n"
  },
  {
    "path": "models/registry.py",
    "content": "_model_entrypoints = {}\n\ndef register_model(fn):\n    model_name = fn.__name__\n    if model_name in _model_entrypoints:\n        raise ValueError(f'model name {model_name} has been registered')\n    _model_entrypoints[model_name] = fn\n\n    return fn\n\ndef model_entrypoint(model_name):\n    try:\n        return _model_entrypoints[model_name]\n    except KeyError as e:\n        print(f'Model Name {model_name} not found')\n\nfrom detectron2.utils.registry import Registry\nMODELITY_INPUT_MAPPER_REGISTRY = Registry(\"MODELITY_INPUT_MAPPER\")\n"
  },
  {
    "path": "output/VIS/cvc/pvt.py",
    "content": "from copy import deepcopy as dcopy\nimport numpy as np\nframe_sampler = {\n    'name': 'VIS_Video_or_Step_To_Clip_TrainMapper',\n    'frames_sampler': {\n        'name': 'Naive_ReferenceFrame_FrameSampler', \n        'clip_sizes': [6],\n        'clip_distribute': 'local_global',\n        'clip_position': 'center',},\n    'clip_global_targets_map_to_local_targets': True, # 把整个视频中这个clip没出现的物体消除\n    'augmentation': {'name': 'WeakPolyP_TrainAug'},}\ntest_mapper_evaluator = {\n    'mapper': {'name': 'VIS_Video_EvalMapper', 'augmentation': {'name': 'WeakPolyP_EvalAug'},},\n    'evaluator':{'name': 'VIS_Evaluator_FrameFast', \n                'frame_metrics': [('mask_dice_iou', {}), ('web', {})], 'video_metrics': [],\n                'metrics_aggregator': ('polyp_metric_aggregator', {}),},}\nattention_defaults = {\n    'attn': {\n        'dropout': 0.1,\n        'nheads': 8,\n        'dim_feedforward': 2048,\n        'activation': 'relu',\n        'normalize_before': False,\n        'enforce_input_proj': True, # try 每个module对input进行proj\n    },\n    'deform_attn':{\n        'nheads': 8,\n        'dim_feedforward': 1024,\n        'activation': 'relu',\n        'dropout': 0.,\n        'enc_n_points': 4\n    },\n}\nd_model = 64\ntrainer_configs = {\n    'eval_seed': 2024, 'model_schedule_seed': 2024, 'stream_idx_seed': 2024,\n    'initckpt':{'path': '', 'load_schedule': False, 'load_model': True, 'load_optimizer': False, 'load_random': False,\n                'eval_init_ckpt': False,},\n    'data':{\n        'evaluate': { '300-tv': dcopy(test_mapper_evaluator), '612-test':dcopy(test_mapper_evaluator), '612-val':dcopy(test_mapper_evaluator),}, \n        'train': { # 3292个clip\n            'Kvasir-train_step[1]': {\n                'mapper': {\n                    'name': 'VIS_Video_or_Step_To_Clip_TrainMapper',\n                    'frames_sampler': {\n                        'name': 'Naive_ReferenceFrame_FrameSampler', \n                        'clip_sizes': [1],\n                        'clip_distribute': 'local_global',\n                        'clip_position': 'center',\n                    },\n                    'clip_global_targets_map_to_local_targets': True, \n                    'augmentation': {'name': 'WeakPolyP_TrainAug_RotateImageToClip',\n                                     'num_frames': 6},\n                },\n            },\n            'Mayo-train_step[6]': {'mapper': dcopy(frame_sampler),},\n            '300-train_step[6]': {'mapper': dcopy(frame_sampler),},\n            '612-train_step[6]': {'mapper': dcopy(frame_sampler),},\n            'polyp_train_step[6]': {\n                'mapper': {\n                    'name': 'VIS_Video_or_Step_To_Clip_TrainMapper',\n                    'frames_sampler': {\n                        'name': 'Naive_ReferenceFrame_FrameSampler', \n                        'clip_sizes': [6],\n                        'clip_distribute': 'local_global',\n                        'clip_position': 'center',\n                    },\n                    'clip_global_targets_map_to_local_targets': True, # 把整个视频中这个clip没出现的物体消除\n                    'augmentation': {'name': 'WeakPolyP_TrainAug'},\n                },\n            },\n        },\n    },\n    'optim': {\n        'splits': [0, None],\n        'batch_sizes': [4], \n        'ckpted_iters': 1260, \n        'one_batch_two_epoch': 'just_use', \n        'scheduler': { 'name': 'multistep_lr', 'milestones':[1260*3, 1260*6, 1260*9, 1260*12], \n                      'gamma': 0.5, 'verbose': False},\n        'name': 'AdamW',\n        'base_lr': 1e-3, 'backbone_lr_multiplier': 0.1, 'weight_decay': 1e-4,\n        'weight_decay_embed': 0.0,\n        'weight_decay_norm': 0.0,\n        'clip_gradients': {\n            'clip_type': 'full_model', # NORM/VALUE # grad.data.clamp_\n            'clip_value': 0.01,\n            'enabled': True,\n            'norm_type': 2.0\n        },\n    },\n    'model': {\n        'name': 'backbone_encoder_decoder_withScaleConsistency',\n        'input_aux':{'video_auxes':[{'name': 'HilbertCurve_FrameQuery','frame_query_number': 20}],  'targets_auxes': [],}, \n        'test_clip_size': None,\n        \"video_backbone\":{\n            'name': 'Video2D_PVT_V2',\n            'freeze': False,\n        },  \n        'fusion': {\n            'name': 'Video_Deform2D_DividedTemporal_MultiscaleEncoder_localGlobal',\n            'd_model': d_model,\n            'video_projs':{\n                'name': 'VideoConv_MultiscaleProj',\n                'projs':{\n                    'res3': {'kernel_size': 1, 'bias': False, 'norm': 'gn_32'},\n                    'res4': {'kernel_size': 1, 'bias': False, 'norm': 'gn_32'},\n                    'res5': {'kernel_size': 1, 'bias': False, 'norm': 'gn_32'},\n                },\n            },\n            'nlayers': 3,\n            'encoded_scales': ['res3', 'res4', 'res5'],\n            'fpn_norm': 'GN',\n            'deform_attn': dcopy(attention_defaults['deform_attn']),\n            'frame_nqueries': 20,\n            'add_local': True,\n            'local_configs': {'d_model': d_model, 'num_heads': 8, \n                              'kernel_size': 5, 'dilation': 1, 'dropout': 0.0,'num_steps': 1},\n            'add_global': True,\n            'global_configs': {'d_model': d_model, 'dim_feedforward': 2048, 'dropout': 0.0,\n                               'scan_order': 'hilbert', 'd_state': 16, 'd_conv': 3, 'nlayers': 3,\n                               'add_attn_mask': False}\n        },            \n        'decoder':{\n            'name': 'Video_MaskedAttn_MultiscaleMaskDecoder_v3',\n            'd_model': d_model,\n            'attn': dcopy(attention_defaults['attn']),\n            'video_nqueries': 10,\n            'inputs_projs': None,\n            'nlayers': 3,\n            'memory_scales': ['res5','res4','res3'],\n            'mask_scale': 'res2',\n            'num_classes': 1,\n            'head_outputs': ['mask', 'class'], # polygon\n            'temporal_self_layer': {\n                'name': 'FrameQuery_SS2DLayer_v2',\n                'd_model': d_model,\n                'nlayers': 3,\n                'dropout': 0.0,\n                'd_state': 16,\n                'd_conv': 3,\n                'dim_feedforward': 2048,\n            },\n            'temporal_cross_layer': {\n                'name': 'TemporalQuery_CrossSelf',\n                'd_model': d_model,\n                'attn': dcopy(attention_defaults['attn']),\n            },\n\n            'loss':{\n                'losses': {\n                    'point_mask_dice_ce': {'num_points': 12544, 'oversample_ratio':3.0, 'importance_sample_ratio': 0.75},\n                    'class_ce': {},\n                }, \n                'matching_metrics': {\n                    'class_prob': {'prob': 2},\n                    # 'mask_dice_ce': {'ce': 2, 'dice': 5},\n                    'point_mask_dice_ce': {'ce':2, 'dice':2, 'num_points':12544}\n                },\n                'aux_layer_weights': 1., # int/list\n                'background_cls_eos': 0.1,\n            }, \n        },\n        'loss_weight': {'mask_dice': 5, \n                        'mask_ce': 2, \n                        'class_ce':2},\n    },\n    \n}\n\n"
  },
  {
    "path": "output/VIS/fibroid/pvt.py",
    "content": "from copy import deepcopy as dcopy\nimport numpy as np\nattention_defaults = {\n    'attn': {\n        'dropout': 0.1,\n        'nheads': 8,\n        'dim_feedforward': 2048,\n        'activation': 'relu',\n        'normalize_before': False,\n        'enforce_input_proj': True, # try 每个module对input进行proj\n    },\n    'deform_attn':{\n        'nheads': 8,\n        'dim_feedforward': 1024,\n        'activation': 'relu',\n        'dropout': 0.,\n        'enc_n_points': 4\n    },\n}\nd_model = 64\ntrainer_configs = {\n    'eval_seed': 2024, 'model_schedule_seed': 2024, 'stream_idx_seed': 2024,\n    'initckpt':{'path': '', 'load_schedule': False, 'load_model': True, 'load_optimizer': False, 'load_random': False,\n                'eval_init_ckpt': False,},\n    'data':{\n        'evaluate': {\n            'fibroid_validate_temp8': {\n                'mapper': {'name': 'VIS_Video_EvalMapper', 'augmentation': {'name': 'WeakPolyP_EvalAug'}, },\n                'evaluator': {'name': 'VIS_Evaluator_FrameFast', \n                              'frame_metrics': [('mask_dice_iou', {}), ('web', {})], 'video_metrics': [],\n                              'metrics_aggregator': ('polyp_metric_aggregator', {}),\n                              },\n            },\n        }, \n        'train': { \n            'fibroid_train_temp8_step[6]': {\n                'mapper': {\n                    'name': 'VIS_Video_or_Step_To_Clip_TrainMapper',\n                    'frames_sampler': {\n                        'name': 'Naive_ReferenceFrame_FrameSampler', \n                        'clip_sizes': [6],\n                        'clip_distribute': 'local_global',\n                        'clip_position': 'center',\n                    },\n                    'clip_global_targets_map_to_local_targets': True, # 把整个视频中这个clip没出现的物体消除\n                    'augmentation': {'name': 'WeakPolyP_TrainAug'},\n                },\n            },\n        },\n    },\n    'optim': {\n        'splits': [0, None],\n        'batch_sizes': [4], \n        'ckpted_iters': 186, \n        'one_batch_two_epoch': 'just_use', \n        'scheduler': { 'name': 'multistep_lr', 'milestones':[186*3, 186*6, 186*9, 186*12], \n                      'gamma': 0.5, 'verbose': False},\n        'name': 'AdamW',\n        'base_lr': 1e-3, 'backbone_lr_multiplier': 0.1, 'weight_decay': 1e-4,\n        'weight_decay_embed': 0.0,\n        'weight_decay_norm': 0.0,\n        'clip_gradients': {\n            'clip_type': 'full_model', # NORM/VALUE # grad.data.clamp_\n            'clip_value': 0.01,\n            'enabled': True,\n            'norm_type': 2.0\n        },\n    },\n    'model': {\n        'name': 'backbone_encoder_decoder_withScaleConsistency',\n        'input_aux':{'video_auxes':[{'name': 'HilbertCurve_FrameQuery','frame_query_number': 20}],  'targets_auxes': [],}, \n        'test_clip_size': None,\n        \"video_backbone\":{\n            'name': 'Video2D_PVT_V2',\n            'freeze': False,\n        },  \n        'fusion': {\n            'name': 'Video_Deform2D_DividedTemporal_MultiscaleEncoder_localGlobal',\n            'd_model': d_model,\n            'video_projs':{\n                'name': 'VideoConv_MultiscaleProj',\n                'projs':{\n                    'res3': {'kernel_size': 1, 'bias': False, 'norm': 'gn_32'},\n                    'res4': {'kernel_size': 1, 'bias': False, 'norm': 'gn_32'},\n                    'res5': {'kernel_size': 1, 'bias': False, 'norm': 'gn_32'},\n                },\n            },\n            'nlayers': 3,\n            'encoded_scales': ['res3', 'res4', 'res5'],\n            'fpn_norm': 'GN',\n            'deform_attn': dcopy(attention_defaults['deform_attn']),\n            'frame_nqueries': 20,\n            'add_local': True,\n            'local_configs': {'d_model': d_model, 'num_heads': 8, \n                              'kernel_size': 5, 'dilation': 2, 'dropout': 0.0,'num_steps': 1},\n            'add_global': True,\n            'global_configs': {'d_model': d_model, 'dim_feedforward': 2048, 'dropout': 0.0,\n                               'scan_order': 'hilbert', 'd_state': 16, 'd_conv': 3, 'nlayers': 3,\n                               'add_attn_mask': True}\n        },            \n        'decoder':{\n            'name': 'Video_MaskedAttn_MultiscaleMaskDecoder_v3',\n            'd_model': d_model,\n            'attn': dcopy(attention_defaults['attn']),\n            'video_nqueries': 10,\n            'inputs_projs': None,\n            'nlayers': 3,\n            'memory_scales': ['res5','res4','res3'],\n            'mask_scale': 'res2',\n            'num_classes': 1,\n            'head_outputs': ['mask', 'class'], # polygon\n            'temporal_self_layer': {\n                'name': 'FrameQuery_SS2DLayer_v2',\n                'd_model': d_model,\n                'nlayers': 3,\n                'dropout': 0.0,\n                'd_state': 16,\n                'd_conv': 3,\n                'dim_feedforward': 2048,\n            },\n            'temporal_cross_layer': {\n                'name': 'TemporalQuery_CrossSelf',\n                'd_model': d_model,\n                'attn': dcopy(attention_defaults['attn']),\n            },\n\n            'loss':{\n                'losses': {\n                    'point_mask_dice_ce': {'num_points': 12544, 'oversample_ratio':3.0, 'importance_sample_ratio': 0.75},\n                    'class_ce': {},\n                }, \n                'matching_metrics': {\n                    'class_prob': {'prob': 2},\n                    # 'mask_dice_ce': {'ce': 2, 'dice': 5},\n                    'point_mask_dice_ce': {'ce':2, 'dice':2, 'num_points':12544}\n                },\n                'aux_layer_weights': 1., # int/list\n                'background_cls_eos': 0.1,\n            }, \n        },\n        'loss_weight': {'mask_dice': 5, \n                        'mask_ce': 2, \n                        'class_ce':2},\n    },\n    \n}\n\n"
  },
  {
    "path": "output/VIS/sunseg/pvt/pvt.py",
    "content": "from copy import deepcopy as dcopy\nimport numpy as np\nattention_defaults = {\n    'attn': {\n        'dropout': 0.1,\n        'nheads': 8,\n        'dim_feedforward': 2048,\n        'activation': 'relu',\n        'normalize_before': False,\n        'enforce_input_proj': True, \n    },\n    'deform_attn':{\n        'nheads': 8,\n        'dim_feedforward': 1024,\n        'activation': 'relu',\n        'dropout': 0.,\n        'enc_n_points': 4\n    },\n}\nd_model = 64\ntrainer_configs = {\n    'eval_seed': 2024, 'model_schedule_seed': 2024, 'stream_idx_seed': 2024,\n    'initckpt':{'path': '', 'load_schedule': False, 'load_model': True, 'load_optimizer': False, 'load_random': False,\n                'eval_init_ckpt': False,},\n    'data':{\n        'evaluate': {\n            'polyp_hard_unseen_validate': {\n                'mapper': {'name': 'VIS_Video_EvalMapper', 'augmentation': {'name': 'WeakPolyP_EvalAug'}, },\n                'evaluator': {'name': 'VIS_Evaluator_FrameFast', \n                              'frame_metrics': [('mask_dice_iou', {}), ('web', {})], 'video_metrics': [],\n                              'metrics_aggregator': ('polyp_metric_aggregator', {}),\n                              },\n            },\n            'polyp_easy_unseen_validate': {\n                'mapper': {'name': 'VIS_Video_EvalMapper', 'augmentation': {'name': 'WeakPolyP_EvalAug'}, },\n                'evaluator': {'name': 'VIS_Evaluator_FrameFast', \n                              'frame_metrics': [('mask_dice_iou', {}), ('web', {})], 'video_metrics': [],\n                              'metrics_aggregator': ('polyp_metric_aggregator', {}),\n                              },\n            },\n            # 'polyp_hard_validate': {\n            #     'mapper': {'name': 'VIS_Video_EvalMapper', 'augmentation': {'name': 'WeakPolyP_EvalAug'}, },\n            #     'evaluator': {'name': 'VIS_Evaluator_FrameFast', \n            #                   'frame_metrics': [('mask_dice_iou', {}), ('web', {})], 'video_metrics': [],\n            #                   'metrics_aggregator': ('polyp_metric_aggregator', {}),\n            #                   },\n            # },\n            # 'polyp_easy_validate': {\n            #     'mapper': {'name': 'VIS_Video_EvalMapper', 'augmentation': {'name': 'WeakPolyP_EvalAug'}, },\n            #     'evaluator': {'name': 'VIS_Evaluator_FrameFast', \n            #                   'frame_metrics': [('mask_dice_iou', {}), ('web', {})], 'video_metrics': [],\n            #                   'metrics_aggregator': ('polyp_metric_aggregator', {}),\n            #                   },\n            # },\n        }, \n        'train': { # 3292个clip\n            'polyp_train_step[6]': {\n                'mapper': {\n                    'name': 'VIS_Video_or_Step_To_Clip_TrainMapper',\n                    'frames_sampler': {\n                        'name': 'Naive_ReferenceFrame_FrameSampler', \n                        'clip_sizes': [6],\n                        'clip_distribute': 'local_global',\n                        'clip_position': 'center',\n                    },\n                    'clip_global_targets_map_to_local_targets': True, # 把整个视频中这个clip没出现的物体消除\n                    'augmentation': {'name': 'WeakPolyP_TrainAug'},\n                },\n            },\n        },\n    },\n    'optim': {\n        'splits': [0, None],\n        'batch_sizes': [4], \n        'ckpted_iters': 823*8, \n        'one_batch_two_epoch': 'just_use', \n        'scheduler': { 'name': 'multistep_lr', 'milestones':[823*3, 823*6, 823*9, 823*12], \n                      'gamma': 0.5, 'verbose': False},\n        'name': 'AdamW',\n        'base_lr': 1e-3, 'backbone_lr_multiplier': 0.1, 'weight_decay': 1e-4,\n        'weight_decay_embed': 0.0,\n        'weight_decay_norm': 0.0,\n        'clip_gradients': {\n            'clip_type': 'full_model', \n            'clip_value': 0.01,\n            'enabled': True,\n            'norm_type': 2.0\n        },\n    },\n    'model': {\n        'name': 'backbone_encoder_decoder_withScaleConsistency',\n        'input_aux':{'video_auxes':[{'name': 'HilbertCurve_FrameQuery','frame_query_number': 20}],  'targets_auxes': [],}, \n        'test_clip_size': None,\n        \"video_backbone\":{\n            'name': 'Video2D_PVT_V2',\n            'freeze': False,\n        },  \n        'fusion': {\n            'name': 'Video_Deform2D_DividedTemporal_MultiscaleEncoder_localGlobal',\n            'd_model': d_model,\n            'video_projs':{\n                'name': 'VideoConv_MultiscaleProj',\n                'projs':{\n                    'res3': {'kernel_size': 1, 'bias': False, 'norm': 'gn_32'},\n                    'res4': {'kernel_size': 1, 'bias': False, 'norm': 'gn_32'},\n                    'res5': {'kernel_size': 1, 'bias': False, 'norm': 'gn_32'},\n                },\n            },\n            'nlayers': 3,\n            'encoded_scales': ['res3', 'res4', 'res5'],\n            'fpn_norm': 'GN',\n            'deform_attn': dcopy(attention_defaults['deform_attn']),\n            'frame_nqueries': 20,\n            'add_local': True,\n            'local_configs': {'d_model': d_model, 'num_heads': 8, \n                              'kernel_size': 5, 'dilation': 1, 'dropout': 0.0,'num_steps': 1},\n            'add_global': True,\n            'global_configs': {'d_model': d_model, 'dim_feedforward': 2048, 'dropout': 0.0,\n                               'scan_order': 'hilbert', 'd_state': 16, 'd_conv': 3, 'nlayers': 3,\n                               'add_attn_mask': False}\n        },            \n        'decoder':{\n            'name': 'Video_MaskedAttn_MultiscaleMaskDecoder_v3',\n            'd_model': d_model,\n            'attn': dcopy(attention_defaults['attn']),\n            'video_nqueries': 10,\n            'inputs_projs': None,\n            'nlayers': 3,\n            'memory_scales': ['res5','res4','res3'],\n            'mask_scale': 'res2',\n            'num_classes': 1,\n            'head_outputs': ['mask', 'class'], # polygon\n            'temporal_self_layer': {\n                'name': 'FrameQuery_SS2DLayer_v2',\n                'd_model': d_model,\n                'nlayers': 3,\n                'dropout': 0.0,\n                'd_state': 16,\n                'd_conv': 3,\n                'dim_feedforward': 2048,\n            },\n            'temporal_cross_layer': {\n                'name': 'TemporalQuery_CrossSelf',\n                'd_model': d_model,\n                'attn': dcopy(attention_defaults['attn']),\n            },\n\n            'loss':{\n                'losses': {\n                    'point_mask_dice_ce': {'num_points': 12544, 'oversample_ratio':3.0, 'importance_sample_ratio': 0.75},\n                    'class_ce': {},\n                }, \n                'matching_metrics': {\n                    'class_prob': {'prob': 2},\n                    # 'mask_dice_ce': {'ce': 2, 'dice': 5},\n                    'point_mask_dice_ce': {'ce':2, 'dice':2, 'num_points':12544}\n                },\n                'aux_layer_weights': 1., # int/list\n                'background_cls_eos': 0.1,\n            }, \n        },\n        'loss_weight': {'mask_dice': 5, \n                        'mask_ce': 5, \n                        'class_ce':2},\n    },\n    \n}\n\n"
  },
  {
    "path": "output/VIS/sunseg/res/res.py",
    "content": "from copy import deepcopy as dcopy\nimport numpy as np\nattention_defaults = {\n    'attn': {\n        'dropout': 0.1,\n        'nheads': 8,\n        'dim_feedforward': 2048,\n        'activation': 'relu',\n        'normalize_before': False,\n        'enforce_input_proj': True, # try 每个module对input进行proj\n    },\n    'deform_attn':{\n        'nheads': 8,\n        'dim_feedforward': 1024,\n        'activation': 'relu',\n        'dropout': 0.,\n        'enc_n_points': 4\n    },\n}\nd_model = 128\ntrainer_configs = {\n    'eval_seed': 2024, 'model_schedule_seed': 2024, 'stream_idx_seed': 2024,\n    'initckpt':{'path': '', 'load_schedule': False, 'load_model': True, 'load_optimizer': False, 'load_random': False,\n                'eval_init_ckpt': False,},\n    'data':{\n        'evaluate': {\n            'polyp_hard_validate': {\n                'mapper': {'name': 'VIS_Video_EvalMapper', 'augmentation': {'name': 'WeakPolyP_EvalAug'}, },\n                'evaluator': {'name': 'VIS_Evaluator_FrameFast', \n                              'frame_metrics': [('mask_dice_iou', {}), ('web', {})], 'video_metrics': [],\n                              'metrics_aggregator': ('polyp_metric_aggregator', {}),\n                              },\n            },\n            'polyp_easy_validate': {\n                'mapper': {'name': 'VIS_Video_EvalMapper', 'augmentation': {'name': 'WeakPolyP_EvalAug'}, },\n                'evaluator': {'name': 'VIS_Evaluator_FrameFast', \n                              'frame_metrics': [('mask_dice_iou', {}), ('web', {})], 'video_metrics': [],\n                              'metrics_aggregator': ('polyp_metric_aggregator', {}),\n                              },\n            },\n        }, \n        'train': { # 3292个clip\n            'polyp_train_step[6]': {\n                'mapper': {\n                    'name': 'VIS_Video_or_Step_To_Clip_TrainMapper',\n                    'frames_sampler': {\n                        'name': 'Naive_ReferenceFrame_FrameSampler', \n                        'clip_sizes': [6],\n                        'clip_distribute': 'local_global',\n                        'clip_position': 'center',\n                    },\n                    'clip_global_targets_map_to_local_targets': True, \n                    'augmentation': {'name': 'WeakPolyP_TrainAug'},\n                },\n            },\n        },\n    },\n    'optim': {\n        'splits': [0, None],\n        'batch_sizes': [4], \n        'ckpted_iters': 823, \n        'one_batch_two_epoch': 'just_use', \n        'scheduler': { 'name': 'multistep_lr', 'milestones':[823*3, 823*6, 823*9, 823*12], \n                      'gamma': 0.5, 'verbose': False},\n        'name': 'AdamW',\n        'base_lr': 1e-3, 'backbone_lr_multiplier': 0.1, 'weight_decay': 1e-4,\n        'weight_decay_embed': 0.0,\n        'weight_decay_norm': 0.0,\n        'clip_gradients': {\n            'clip_type': 'full_model', \n            'clip_value': 0.01,\n            'enabled': True,\n            'norm_type': 2.0\n        },\n    },\n    'model': {\n        'test_clip_size': None,\n        'name': 'backbone_encoder_decoder_withScaleConsistency',\n        'input_aux':{'video_auxes':[{'name': 'HilbertCurve_FrameQuery','frame_query_number': 20}],  'targets_auxes': [],}, \n\n        \"video_backbone\":{\n            'name': 'Res2Net_50_EachFrame',\n            'freeze': False,\n        },  \n        'fusion': {\n            'name': 'Video_Deform2D_DividedTemporal_MultiscaleEncoder_localGlobal',\n            'd_model': d_model,\n            'video_projs':{\n                'name': 'VideoConv_MultiscaleProj',\n                'projs':{\n                    'res3': {'kernel_size': 1, 'bias': False, 'norm': 'gn_32'},\n                    'res4': {'kernel_size': 1, 'bias': False, 'norm': 'gn_32'},\n                    'res5': {'kernel_size': 1, 'bias': False, 'norm': 'gn_32'},\n                },\n            },\n            'nlayers': 3,\n            'encoded_scales': ['res3', 'res4', 'res5'],\n            'fpn_norm': 'GN',\n            'deform_attn': dcopy(attention_defaults['deform_attn']),\n            'frame_nqueries': 20,\n            'add_local': True,\n            'local_configs': {'d_model': d_model, 'num_heads': 8, \n                              'kernel_size': 5, 'dilation': 1, 'dropout': 0.0,'num_steps': 1},\n            'add_global': True,\n            'global_configs': {'d_model': d_model, 'dim_feedforward': 2048, 'dropout': 0.0,\n                               'scan_order': 'hilbert', 'd_state': 16, 'd_conv': 3, 'nlayers': 3,\n                               'add_attn_mask': False}\n        },            \n        'decoder':{\n            'name': 'Video_MaskedAttn_MultiscaleMaskDecoder_v3',\n            'd_model': d_model,\n            'attn': dcopy(attention_defaults['attn']),\n            'video_nqueries': 10,\n            'inputs_projs': None,\n            'nlayers': 3,\n            'memory_scales': ['res5','res4','res3'],\n            'mask_scale': 'res2',\n            'num_classes': 1,\n            'head_outputs': ['mask', 'class'], # polygon\n            'temporal_self_layer': {\n                'name': 'FrameQuery_SS2DLayer_v2',\n                'd_model': d_model,\n                'nlayers': 3,\n                'dropout': 0.0,\n                'd_state': 16,\n                'd_conv': 3,\n                'dim_feedforward': 2048,\n            },\n            'temporal_cross_layer': {\n                'name': 'TemporalQuery_CrossSelf',\n                'd_model': d_model,\n                'attn': dcopy(attention_defaults['attn']),\n            },\n\n            'loss':{\n                'losses': {\n                    'point_mask_dice_ce': {'num_points': 12544, 'oversample_ratio':3.0, 'importance_sample_ratio': 0.75},\n                    'class_ce': {},\n                }, \n                'matching_metrics': {\n                    'class_prob': {'prob': 2},\n                    # 'mask_dice_ce': {'ce': 2, 'dice': 5},\n                    'point_mask_dice_ce': {'ce':2, 'dice':2, 'num_points':12544}\n                },\n                'aux_layer_weights': 1., # int/list\n                'background_cls_eos': 0.1,\n            }, \n        },\n        'loss_weight': {'mask_dice': 5, \n                        'mask_ce': 2, \n                        'class_ce':2},\n    },\n    \n}\n\n"
  },
  {
    "path": "reorganize_sunseg.py",
    "content": "import os, shutil, glob\nfrom tqdm import tqdm\n\nSUN_root = f\"{os.getenv('DATASET_PATH')}/SUN-SEG/SUN-Positive/\"\nSUNSEG_root = f\"{os.getenv('DATASET_PATH')}/SUN-SEG/SUN-SEG-Annotation/\"\n\nSUN_split_dict = {}\nSUNSEG_split_dict = {}\nSUNSEG_dataset_dict = {}\nimage_list = []\n\n# SUN_list = glob.glob(SUN_root + '*/*.jpg')\nSUNSEG_test_list = glob.glob(SUNSEG_root + 'Test*/*/GT/*/*.png')\nSUNSEG_train_list = glob.glob(SUNSEG_root + 'TrainDataset/GT/*/*.png')\nSUNSEG_list = SUNSEG_test_list + SUNSEG_train_list\n\nSUN_list = [os.path.join(SUN_root, name.split('/')[-2].split('_')[0] if len(name.split('/')[-2].split('_')) > 1 else name.split('/')[-2], name.split('/')[-1].replace('.png', '')) for name in SUNSEG_list]\n\nfor SUN_path, SUNSEG_path in zip(SUN_list, SUNSEG_list):\n    \"\"\"\n        @func: Get SUN and SUN-SEG case-to-image structure in a dictionary\n    \"\"\"\n\n    SUN_case_name, SUN_image_name = SUN_path.split('/')[-2], SUN_path.split('/')[-1]\n    SUNSEG_dataset_name, SUNSEG_case_name, SUNSEG_image_name = SUNSEG_path.split('SUN-SEG-Annotation/')[1].split('/GT')[0], SUNSEG_path.split('/')[-2], SUNSEG_path.split('/')[-1].rstrip('.png')\n\n    SUN_split_dict[SUN_image_name] = SUN_case_name\n    SUNSEG_split_dict[SUNSEG_image_name] = SUNSEG_case_name\n    SUNSEG_dataset_dict[SUNSEG_image_name] = SUNSEG_dataset_name\n\n    image_list.append(SUN_image_name)\n\nfor image in tqdm(image_list):\n    \"\"\"\n        @func: Change original SUN's structure\n    \"\"\"\n    SUN_case = SUN_split_dict[image]\n    SUNSEG_case = SUNSEG_split_dict[image]\n    dataset_split = SUNSEG_dataset_dict[image]\n\n    os.makedirs(os.path.join(SUNSEG_root, dataset_split, 'Frame', SUNSEG_case), exist_ok=True)\n\n    shutil.move(os.path.join(SUN_root, SUN_case, image + '.jpg'),\n                os.path.join(SUNSEG_root, dataset_split, 'Frame', SUNSEG_case, image + '.jpg'))\n\n\n# combine Seen/Unseen to same directory\nos.makedirs(os.path.join(os.getenv('DATASET_PATH'), 'SUN-SEG/SUN-SEG-Annotation', 'TestEasyDataset', 'combine/Frame'), exist_ok=True)\nos.makedirs(os.path.join(os.getenv('DATASET_PATH'), 'SUN-SEG/SUN-SEG-Annotation', 'TestEasyDataset', 'combine/GT'), exist_ok=True)\nos.system('mv $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestEasyDataset/Seen/Frame/* $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestEasyDataset/combine/Frame/')\nos.system('mv $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestEasyDataset/Unseen/Frame/* $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestEasyDataset/combine/Frame/')\n\nos.system('mv $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestEasyDataset/Seen/GT/*   $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestEasyDataset/combine/GT/')\nos.system('mv $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestEasyDataset/Unseen/GT/* $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestEasyDataset/combine/GT/')\n\nos.makedirs(os.path.join(os.getenv('DATASET_PATH'), 'SUN-SEG/SUN-SEG-Annotation', 'TestHardDataset', 'combine/Frame'), exist_ok=True)\nos.makedirs(os.path.join(os.getenv('DATASET_PATH'), 'SUN-SEG/SUN-SEG-Annotation', 'TestHardDataset', 'combine/GT'), exist_ok=True)\nos.system('mv $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestHardDataset/Seen/Frame/* $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestHardDataset/combine/Frame/')\nos.system('mv $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestHardDataset/Unseen/Frame/* $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestHardDataset/combine/Frame/')\n\nos.system('mv $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestHardDataset/Seen/GT/* $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestHardDataset/combine/GT/')\nos.system('mv $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestHardDataset/Unseen/GT/* $DATASET_PATH/SUN-SEG/SUN-SEG-Annotation/TestHardDataset/combine/GT/')\n"
  },
  {
    "path": "trainers/Trainer.py",
    "content": "import torch\nimport numpy as np\nimport random\nimport math\nimport logging\nimport time\nimport os\nfrom utils.misc import reduce_dict, to_device, is_dist_avail_and_initialized\nimport gc\nfrom utils.misc import  SmoothedValue, MetricLogger\nfrom torch.nn.parallel import DistributedDataParallel as DDP\nimport detectron2.utils.comm as comm\nimport datetime\nimport torch.distributed as dist\nfrom models import model_entrypoint\nfrom utils.misc import to_device\n\n__all__ = ['Trainer']\nclass Trainer:\n    def __init__(self, configs):\n        torch.autograd.set_detect_anomaly(False)\n        torch.backends.cuda.matmul.allow_tf32 = True\n        torch.backends.cudnn.allow_tf32 = True\n        torch.backends.cudnn.benchmark = True\n        seed = configs['model_schedule_seed']\n        random.seed(seed)\n        np.random.seed(seed)\n        torch.random.manual_seed(seed)\n        with torch.cuda.device(self.device):\n            torch.cuda.manual_seed(seed)        \n\n        # model and data\n        create_model_schedule = model_entrypoint(configs['model']['name'])\n        self.model, self.optimizer, self.scheduler, \\\n            self.train_samplers, self.train_loaders, self.log_lr_group_name_to_idx, \\\n        self.eval_function = create_model_schedule(configs, device=self.device,)\n        self.register_metric_logger([f'lr_group_{haosen}' for haosen in list(self.log_lr_group_name_to_idx.keys())])\n        logging.debug(f'total number of parameters:{sum(p.numel() for p in self.model.parameters())}')\n        logging.debug(f'total number of trainable parameters:{sum(p.numel() for p in self.model.parameters() if p.requires_grad)}')\n        logging.debug(configs)\n        self.eval_seed = configs['eval_seed']\n        self.out_dir = configs['out_dir']\n        self.ckpted_iters = configs['optim']['ckpted_iters'] # list[int]\n        self.num_iterations = 0 \n        self.num_samples = 0\n        assert self.train_samplers[0].start_idx == self.num_samples\n        if comm.get_world_size() > 1:\n            self.model = DDP(self.model, device_ids=[comm.get_local_rank()], find_unused_parameters=False, broadcast_buffers = False)\n    \n        random.seed(seed + comm.get_rank())\n        np.random.seed(seed + comm.get_rank())\n        torch.random.manual_seed(seed + comm.get_rank())\n        with torch.cuda.device(self.device):\n            torch.cuda.manual_seed(seed + comm.get_rank()) \n\n        if configs['initckpt']['path'] != '':\n            self.load_ckpt(configs['initckpt']['path'], \n                           load_random=configs['initckpt']['load_random'], \n                           load_model=configs['initckpt']['load_model'], \n                           load_schedule=configs['initckpt']['load_schedule'], \n                           load_optimize=configs['initckpt']['load_optimizer'])\n        self.save_ckpt() \n        if configs['initckpt']['eval_init_ckpt']:\n            self.evaluate() \n            self.load_ckpt(os.path.join(self.iteration_dir, 'ckpt.pth.tar'), \n                       load_random=True, load_schedule=False, load_model=False, load_optimize=False,)\n\n    def train(self):   \n        manual_stop_train = False\n        for loader in self.train_loaders:\n            for idx, batch_dict in enumerate(loader):\n                if manual_stop_train:\n                    self.save_ckpt()\n                self.model.train()\n                meta_idxs = batch_dict.pop('meta_idxs')\n                visualize = batch_dict.pop('visualize')\n                batch_dict = to_device(batch_dict, self.device)\n                batch_dict['visualize_paths'] = self.visualize_path(meta_idxs=meta_idxs, \n                                                                    visualize=visualize) \n                iteration_time = time.time()\n                loss_dict_unscaled, loss_weight = self.model(batch_dict)\n                loss = sum([loss_dict_unscaled[k] * loss_weight[k] for k in loss_weight.keys()])\n                assert math.isfinite(loss.item()), f\"Loss is {loss.item()}, stopping training\"\n                loss.backward()       \n                self.optimizer.step()\n                iteration_time = time.time() - iteration_time\n                self.optimizer.zero_grad(set_to_none=True)\n                self.scheduler.step() \n                sample_idxs = comm.all_gather(meta_idxs) \n                sample_idxs = [taylor for cardib in sample_idxs for taylor in cardib]\n                self.num_samples += len(sample_idxs)\n                self.num_iterations += 1\n                loss_dict_unscaled_item = {key: torch.tensor(value.detach().item(), device=self.device) for key, value in loss_dict_unscaled.items()}\n                del loss, loss_dict_unscaled\n                self._log(loss_dict_unscaled=loss_dict_unscaled_item,\n                          loss_weight=loss_weight,\n                          sample_idxs=sample_idxs,\n                          iteration_time=iteration_time)\n    def save_ckpt(self):\n        rng_state_dict = {comm.get_rank(): {\n            'cpu_rng_state': torch.get_rng_state(),\n            'gpu_rng_state': torch.cuda.get_rng_state(self.device),\n            'numpy_rng_state': np.random.get_state(),\n            'py_rng_state': random.getstate()\n        }}\n\n        rng_state_dict_by_rank = comm.gather(rng_state_dict, dst=0)\n\n        if comm.is_main_process():\n            rng_state_dict_by_rank = {key : value for rs in rng_state_dict_by_rank for key,value in rs.items()}\n            model_without_ddp = self.model.module if isinstance(self.model, DDP) else self.model\n            checkpoint_dict = {\n                'model': model_without_ddp.state_dict(),\n                'optimizer': self.optimizer.state_dict(),\n                'scheduler': self.scheduler.state_dict(),\n                'num_samples': self.num_samples,\n                'num_iterations': self.num_iterations,\n                'rng_state_dict_by_rank': rng_state_dict_by_rank, \n                'metrics': {},\n            }\n            os.makedirs(self.iteration_dir, exist_ok=True)\n            torch.save(checkpoint_dict, os.path.join(self.iteration_dir, 'ckpt.pth.tar'))\n            del checkpoint_dict\n        if is_dist_avail_and_initialized():\n            dist.barrier()\n        del rng_state_dict_by_rank\n\n    @torch.no_grad()\n    def evaluate(self):\n        random.seed(self.eval_seed)\n        np.random.seed(self.eval_seed)\n        torch.random.manual_seed(self.eval_seed)\n        with torch.cuda.device(self.device):\n            torch.cuda.manual_seed(self.eval_seed)     \n        self.model.eval()\n        eval_model = self.model.module if isinstance(self.model, DDP) else self.model\n        ckpt_file = os.path.join(self.iteration_dir, 'ckpt.pth.tar')\n        assert os.path.exists(ckpt_file), 'must save ckpt before evaluate'\n        evaluate_metrics = self.eval_function(model = eval_model,  output_dir = self.iteration_dir)\n        if is_dist_avail_and_initialized():\n            dist.barrier()\n        if comm.is_main_process():\n            checkpoint_dict = torch.load(ckpt_file, map_location='cpu')\n            ckpt_metrics = checkpoint_dict['metrics']\n            to_update_metrics = {}\n            for metric_key in evaluate_metrics.keys():\n                metric_value = evaluate_metrics[metric_key]\n                if metric_key in ckpt_metrics:\n                    saved_value = ckpt_metrics[metric_key]\n                    if (metric_value - saved_value) > 1e-2:\n                        logging.error(f'{metric_key} different saved value')\n                        to_update_metrics[metric_key] = metric_value\n                else:\n                    to_update_metrics[metric_key] = metric_value\n            checkpoint_dict['metrics'] = evaluate_metrics\n            metric_string = ' '.join([f'{key} : {value:.6f}' for key, value in evaluate_metrics.items()])\n            logging.debug(metric_string)\n            torch.save(checkpoint_dict, ckpt_file)\n            del checkpoint_dict\n\n        if is_dist_avail_and_initialized():\n            dist.barrier()\n\n    def load_ckpt(self, \n                  ckpt_path=None, \n                  load_schedule=False,\n                  load_optimize=False,  \n                  load_model=False,\n                  load_random=False, \n                  ):\n        assert os.path.exists(ckpt_path)\n        model_without_ddp = self.model.module if isinstance(self.model, DDP) else self.model\n        checkpoint = torch.load(ckpt_path, map_location='cpu')\n\n        if load_model:\n            model_without_ddp.load_state_dict(checkpoint['model'], strict=True)\n            \n        if load_optimize:\n            self.optimizer.load_state_dict(checkpoint['optimizer'])\n            self.scheduler.load_state_dict(checkpoint['scheduler'])\n\n        if load_schedule:\n            self.num_samples = checkpoint['num_samples'] \n            self.num_iterations = checkpoint['num_iterations'] \n\n            sampler = self.train_samplers[0]\n            while (sampler.end_idx != None) and (self.num_samples > (sampler.end_idx - 1)):\n                self.train_samplers.pop(0)\n                self.train_loaders.pop(0)\n                sampler = self.train_samplers[0]\n            self.train_samplers[0].set_iter_first_sample_idx(self.num_samples)\n\n        if load_random:\n            rng_state_dict_by_rank = checkpoint['rng_state_dict_by_rank']\n            torch.set_rng_state(rng_state_dict_by_rank[comm.get_rank()]['cpu_rng_state'])\n            torch.cuda.set_rng_state(rng_state_dict_by_rank[comm.get_rank()]['gpu_rng_state'], device=self.device)\n            np.random.set_state(rng_state_dict_by_rank[comm.get_rank()]['numpy_rng_state'])\n            random.setstate(rng_state_dict_by_rank[comm.get_rank()]['py_rng_state'])\n\n        del checkpoint\n\n    def _log(self, \n             loss_dict_unscaled,\n             loss_weight,\n             sample_idxs, \n             iteration_time,):\n        loss_dict_unscaled_reduced = reduce_dict(loss_dict_unscaled) \n        loss_value = sum([loss_dict_unscaled_reduced[key] * loss_weight[key] for key in loss_weight.keys()])\n\n        if comm.is_main_process():\n            for idx, sp_idx in enumerate(sample_idxs):\n                pass\n            logger_updates = {}\n            for log_lr_group_name, log_lr_group_idx in self.log_lr_group_name_to_idx.items():\n                if log_lr_group_idx is None:\n                    logger_updates[f'lr_group_{log_lr_group_name}'] = 0\n                else:\n                    logger_updates[f'lr_group_{log_lr_group_name}'] = self.optimizer.param_groups[log_lr_group_idx][\"lr\"]\n            \n            logger_updates.update(loss_dict_unscaled_reduced)\n            logger_updates.update({\n                'loss_value': loss_value,\n                'iteration_time': iteration_time,\n            })\n            self.metric_logger.update(**logger_updates)\n            log_string = self.log_header(iteration_time, sample_idxs) + f'\\n{str(self.metric_logger)}'\n            wandb_log = self.metric_logger.to_dict()\n            logging.debug(log_string)\n\n        if is_dist_avail_and_initialized():\n            dist.barrier()\n\n        if type(self.ckpted_iters) == int:\n            do_ckpt = (self.num_iterations % self.ckpted_iters) == 0\n        elif type(self.ckpted_iters) == list:\n            do_ckpt = self.num_iterations in self.ckpted_iters\n        else:\n            raise ValueError()\n        if (self.num_iterations % 2000 == 0) or do_ckpt:\n            gc.collect()\n            torch.cuda.empty_cache()\n        if do_ckpt:\n            try: \n                self.save_ckpt() \n                self.evaluate()\n                self.load_ckpt(os.path.join(self.iteration_dir, 'ckpt.pth.tar'),  \n                            load_random=True, load_schedule=False, load_model=False, load_optimize=False,)\n            except:\n                if comm.is_main_process():\n                    logging.error(f'Iteration {self.num_iterations} evaluate error')\n        if is_dist_avail_and_initialized():\n            dist.barrier()\n\n\n    @property\n    def device(self):\n        return torch.device(comm.get_local_rank())\n    \n    @property\n    def iteration_dir(self):\n        return os.path.join(self.out_dir, f'epc[{self.epoch[-1]}]_iter[{self.num_iterations}]_sap[{self.num_samples}]')\n\n    @property\n    def epoch(self):\n        dataset_length = len(self.train_loaders[0].dataset)\n        epoch = self.num_samples / dataset_length\n        int_part, dec_part = f'{epoch:.2f}'.split('.')\n        return epoch, f'{int_part}_{dec_part}'\n\n    def log_header(self, iteration_time, sample_idxs):\n        one_epoch_iterations = len(self.train_loaders[0].dataset) // len(sample_idxs)\n        eta = datetime.timedelta(seconds=one_epoch_iterations * iteration_time)\n        return f'Epoch_ETA: [{str(eta)}] Epoch:[{self.epoch[0]:.2f}] Iter: [{(self.num_iterations):06d}] Sample: [{self.num_samples:06d}]'\n \n    def visualize_path(self, meta_idxs, visualize):\n        return [os.path.join(self.iteration_dir, 'visualize_model', f'train_meta_{str(meta_idx)}') if vis else None for (meta_idx, vis) in zip(meta_idxs, visualize)]\n\n    def register_metric_logger(self, log_keys):\n        if comm.is_main_process():\n            if not hasattr(self, 'metric_logger'):\n                self.metric_logger = MetricLogger(delimiter='\\t')\n\n            for haosen in log_keys:\n                if 'lr_group' in haosen:\n                    self.metric_logger.add_meter(haosen, SmoothedValue(window_size=1,fmt='{value:.8f}', handler='value'))\n                elif haosen == 'iteration_time':\n                    self.metric_logger.add_meter(haosen, SmoothedValue(window_size=1,fmt='{value:2f}',handler='value'))\n                else:\n                    raise ValueError()"
  },
  {
    "path": "trainers/__init__.py",
    "content": "\nfrom .Trainer import Trainer \ntask_to_trainer = {\n    'VIS': Trainer,\n    \n}\n\n\n"
  },
  {
    "path": "utils/__init__.py",
    "content": "from . import misc"
  },
  {
    "path": "utils/misc.py",
    "content": "# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n\"\"\"\nMisc functions, including distributed helpers.\n\nMostly copy-paste from torchvision references.\n\nchange SmoothedValue and MetricLogger, interpolate\nmodify nested_tensor_from_tensor_list\nadd nested_tensor_from_videos_list\n\"\"\"\nimport os\nimport subprocess\nimport time\nfrom collections import defaultdict, deque\nimport datetime\nimport pickle\nfrom packaging import version\nfrom typing import Optional, List\n\nimport torch\nimport torch.distributed as dist\nfrom torch import Tensor\n\n# needed due to empty tensor bug in pytorch and torchvision 0.5\nimport torchvision\nif version.parse(torchvision.__version__) < version.parse('0.7'):\n    from torchvision.ops import _new_empty_tensor\n    from torchvision.ops.misc import _output_size\n\n\nclass SmoothedValue(object):\n    \"\"\"Track a series of values and provide access to smoothed values over a\n    window or the global series average.\n    \"\"\"\n\n    def __init__(self, window_size=1, fmt='{value:.6f}', handler='value'):\n        if fmt is None:\n            fmt = \"{median:.4f} ({global_avg:.4f})\"\n        self.deque = deque(maxlen=window_size)\n        self.total = 0.0\n        self.count = 0\n        self.fmt = fmt\n        self.handler = handler\n\n    def update(self, value, n=1):\n        self.deque.append(value)\n        self.count += n\n        self.total += value * n\n\n    def synchronize_between_processes(self):\n        \"\"\"\n        Warning: does not synchronize the deque!\n        \"\"\"\n        if not is_dist_avail_and_initialized():\n            return\n        t = torch.tensor([self.count, self.total], dtype=torch.float64, device='cuda')\n        dist.barrier()\n        dist.all_reduce(t)\n        t = t.tolist()\n        self.count = int(t[0])\n        self.total = t[1]\n\n    @property\n    def median(self):\n        d = torch.tensor(list(self.deque))\n        return d.median().item()\n\n    @property\n    def avg(self):\n        d = torch.tensor(list(self.deque), dtype=torch.float32)\n        return d.mean().item()\n\n    @property\n    def global_avg(self):\n        return self.total / self.count\n\n    @property\n    def max(self):\n        return max(self.deque)\n\n    @property\n    def value(self):\n        return self.deque[-1]\n    \n    @property\n    def wandb_log_property(self):\n        if self.handler == 'value':\n            return self.value\n        elif self.handler == 'avg':\n            return self.avg\n        else:\n            raise NotImplementedError('other log property not implemented!')\n        \n    def __str__(self):\n        return self.fmt.format(\n            median=self.median,\n            avg=self.avg,\n            global_avg=self.global_avg,\n            max=self.max,\n            value=self.value)\n\n\ndef all_gather(data):\n    \"\"\"\n    Run all_gather on arbitrary picklable data (not necessarily tensors)\n    Args:\n        data: any picklable object\n    Returns:\n        list[data]: list of data gathered from each rank\n    \"\"\"\n    world_size = get_world_size()\n    if world_size == 1:\n        return [data]\n\n    # serialized to a Tensor\n    buffer = pickle.dumps(data)\n    storage = torch.ByteStorage.from_buffer(buffer)\n    tensor = torch.ByteTensor(storage).to(\"cuda\")\n\n    # obtain Tensor size of each rank\n    local_size = torch.tensor([tensor.numel()], device=\"cuda\")\n    size_list = [torch.tensor([0], device=\"cuda\") for _ in range(world_size)]\n    dist.all_gather(size_list, local_size)\n    size_list = [int(size.item()) for size in size_list]\n    max_size = max(size_list)\n\n    # receiving Tensor from all ranks\n    # we pad the tensor because torch all_gather does not support\n    # gathering tensors of different shapes\n    tensor_list = []\n    for _ in size_list:\n        tensor_list.append(torch.empty((max_size,), dtype=torch.uint8, device=\"cuda\"))\n    if local_size != max_size:\n        padding = torch.empty(size=(max_size - local_size,), dtype=torch.uint8, device=\"cuda\")\n        tensor = torch.cat((tensor, padding), dim=0)\n    dist.all_gather(tensor_list, tensor)\n\n    data_list = []\n    for size, tensor in zip(size_list, tensor_list):\n        buffer = tensor.cpu().numpy().tobytes()[:size]\n        data_list.append(pickle.loads(buffer))\n\n    return data_list\n\ndef all_gather_cpu(data):\n    world_size = get_world_size()\n    if world_size == 1:\n        return [data]\n\n    # serialized to a Tensor\n    buffer = pickle.dumps(data)\n    storage = torch.ByteStorage.from_buffer(buffer)\n    tensor = torch.ByteTensor(storage)\n\n    # obtain Tensor size of each rank\n    local_size = torch.tensor([tensor.numel()])\n    size_list = [torch.tensor([0]) for _ in range(world_size)]\n    dist.all_gather(size_list, local_size)\n    size_list = [int(size.item()) for size in size_list]\n    max_size = max(size_list)\n\n    # receiving Tensor from all ranks\n    # we pad the tensor because torch all_gather does not support\n    # gathering tensors of different shapes\n    tensor_list = []\n    for _ in size_list:\n        tensor_list.append(torch.empty((max_size,), dtype=torch.uint8))\n    if local_size != max_size:\n        padding = torch.empty(size=(max_size - local_size,), dtype=torch.uint8)\n        tensor = torch.cat((tensor, padding), dim=0)\n    dist.all_gather(tensor_list, tensor)\n\n    data_list = []\n    for size, tensor in zip(size_list, tensor_list):\n        buffer = tensor.cpu().numpy().tobytes()[:size]\n        data_list.append(pickle.loads(buffer))\n\n    return data_list\n\n\ndef reduce_dict(input_dict, average=True):\n    \"\"\"\n    Args:\n        input_dict (dict): all the values will be reduced\n        average (bool): whether to do average or sum\n    Reduce the values in the dictionary from all processes so that all processes\n    have the averaged results. Returns a dict with the same fields as\n    input_dict, after reduction.\n    \"\"\"\n    world_size = get_world_size()\n    if world_size < 2:\n        return input_dict\n    with torch.no_grad():\n        names = []\n        values = []\n        # sort the keys so that they are consistent across processes\n        for k in sorted(input_dict.keys()):\n            names.append(k)\n            values.append(input_dict[k])\n        values = torch.stack(values, dim=0)\n        dist.all_reduce(values)\n        if average:\n            values /= world_size\n        reduced_dict = {k: v for k, v in zip(names, values)}\n    return reduced_dict\n\ndef reduce_scalar(input, average=True):\n    \"\"\"\n    Args:\n        input: all the values will be reduced\n        average (bool): whether to do average or sum\n    Reduce the values in the dictionary from all processes so that all processes\n    have the averaged results. Returns a dict with the same fields as\n    input, after reduction.\n    \"\"\"\n    world_size = get_world_size()\n    if world_size < 2:\n        return input.item()\n    with torch.no_grad():\n        dist.all_reduce(input)\n        if average:\n            input /= world_size\n        return input.item()\n\n\nclass MetricLogger(object):\n    def __init__(self, delimiter=\"\\t\"):\n        self.meters = defaultdict(SmoothedValue)\n        self.delimiter = delimiter\n\n    def update(self, **kwargs):\n        for k, v in kwargs.items():\n            if isinstance(v, torch.Tensor):\n                v = v.item()\n            assert isinstance(v, (float, int))\n            self.meters[k].update(v)\n\n    def __getattr__(self, attr):\n        if attr in self.meters:\n            return self.meters[attr]\n        if attr in self.__dict__:\n            return self.__dict__[attr]\n        raise AttributeError(\"'{}' object has no attribute '{}'\".format(\n            type(self).__name__, attr))\n\n    def __str__(self):\n        loss_str = []\n        for name, meter in self.meters.items():\n            loss_str.append(\n                \"{}: {}\".format(name, str(meter))\n            )\n        return self.delimiter.join(loss_str)\n    \n    def to_dict(self):\n        log_dict = {}\n        for name in self.meters.keys():\n            log_dict[name] = self.meters[name].wandb_log_property\n        return log_dict\n    \n    def synchronize_between_processes(self):\n        for meter in self.meters.values():\n            meter.synchronize_between_processes()\n\n    def add_meter(self, name, meter):\n        self.meters[name] = meter\n\n    def log_every(self, iterable, print_freq, header=None):\n        i = 0\n        if not header:\n            header = ''\n        start_time = time.time()\n        end = time.time()\n        iter_time = SmoothedValue(fmt='{avg:.4f}')\n        data_time = SmoothedValue(fmt='{avg:.4f}')\n        space_fmt = ':' + str(len(str(len(iterable)))) + 'd'\n        if torch.cuda.is_available():\n            log_msg = self.delimiter.join([\n                header,\n                '[{0' + space_fmt + '}/{1}]',\n                'eta: {eta}',\n                '{meters}',\n                'time: {time}',\n                'data: {data}',\n                'max mem: {memory:.0f}'\n            ])\n        else:\n            log_msg = self.delimiter.join([\n                header,\n                '[{0' + space_fmt + '}/{1}]',\n                'eta: {eta}',\n                '{meters}',\n                'time: {time}',\n                'data: {data}'\n            ])\n        MB = 1024.0 * 1024.0\n        for obj in iterable:\n            data_time.update(time.time() - end)\n            yield obj\n            iter_time.update(time.time() - end)\n            if i % print_freq == 0 or i == len(iterable) - 1:\n                eta_seconds = iter_time.global_avg * (len(iterable) - i)\n                eta_string = str(datetime.timedelta(seconds=int(eta_seconds)))\n                if torch.cuda.is_available():\n                    print(log_msg.format(\n                        i, len(iterable), eta=eta_string,\n                        meters=str(self),\n                        time=str(iter_time), data=str(data_time),\n                        memory=torch.cuda.max_memory_allocated() / MB))\n                else:\n                    print(log_msg.format(\n                        i, len(iterable), eta=eta_string,\n                        meters=str(self),\n                        time=str(iter_time), data=str(data_time)))\n            i += 1\n            end = time.time()\n        total_time = time.time() - start_time\n        total_time_str = str(datetime.timedelta(seconds=int(total_time)))\n        print('{} Total time: {} ({:.4f} s / it)'.format(\n            header, total_time_str, total_time / len(iterable)))\n\n\ndef get_sha():\n    cwd = os.path.dirname(os.path.abspath(__file__))\n\n    def _run(command):\n        return subprocess.check_output(command, cwd=cwd).decode('ascii').strip()\n    sha = 'N/A'\n    diff = \"clean\"\n    branch = 'N/A'\n    try:\n        sha = _run(['git', 'rev-parse', 'HEAD'])\n        subprocess.check_output(['git', 'diff'], cwd=cwd)\n        diff = _run(['git', 'diff-index', 'HEAD'])\n        diff = \"has uncommited changes\" if diff else \"clean\"\n        branch = _run(['git', 'rev-parse', '--abbrev-ref', 'HEAD'])\n    except Exception:\n        pass\n    message = f\"sha: {sha}, status: {diff}, branch: {branch}\"\n    return message\n\n\ndef collate_fn(batch):\n    batch = list(zip(*batch))\n    batch[0] = nested_tensor_from_tensor_list(batch[0])\n    return tuple(batch)\n\n\ndef _max_by_axis(the_list):\n    # type: (List[List[int]]) -> List[int]\n    maxes = the_list[0]\n    for sublist in the_list[1:]:\n        for index, item in enumerate(sublist):\n            maxes[index] = max(maxes[index], item)\n    return maxes\n\n\nclass NestedTensor(object):\n    def __init__(self, tensors, mask: Optional[Tensor]):\n        self.tensors = tensors\n        self.mask = mask\n\n    def to(self, device):\n        # type: (Device) -> NestedTensor # noqa\n        cast_tensor = self.tensors.to(device)\n        mask = self.mask\n        if mask is not None:\n            assert mask is not None\n            cast_mask = mask.to(device)\n        else:\n            cast_mask = None\n        return NestedTensor(cast_tensor, cast_mask)\n\n    def decompose(self):\n        return self.tensors, self.mask\n\n    def __repr__(self):\n        return str(self.tensors)\n\n# region change this function\n# def nested_tensor_from_tensor_list(tensor_list: List[Tensor]):\n#     # TODO make this more general\n#     if tensor_list[0].ndim == 3:\n#         if torchvision._is_tracing():\n#             # nested_tensor_from_tensor_list() does not export well to ONNX\n#             # call _onnx_nested_tensor_from_tensor_list() instead\n#             return _onnx_nested_tensor_from_tensor_list(tensor_list)\n\n#         # TODO make it support different-sized images\n#         max_size = _max_by_axis([list(img.shape) for img in tensor_list])\n#         # min_size = tuple(min(s) for s in zip(*[img.shape for img in tensor_list]))\n#         batch_shape = [len(tensor_list)] + max_size\n#         b, c, h, w = batch_shape\n#         dtype = tensor_list[0].dtype\n#         device = tensor_list[0].device\n#         tensor = torch.zeros(batch_shape, dtype=dtype, device=device)\n#         mask = torch.ones((b, h, w), dtype=torch.bool, device=device)\n#         for img, pad_img, m in zip(tensor_list, tensor, mask):\n#             pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img)\n#             m[: img.shape[1], :img.shape[2]] = False\n#     else:\n#         raise ValueError('not supported')\n#     return NestedTensor(tensor, mask)\n# endregion\n\ndef nested_tensor_from_tensor_list(tensor_list: List[Tensor]):\n    \"\"\"\n    This function receives a list of image tensors and returns a NestedTensor of the padded images, along with their\n    padding masks (true for padding areas, false otherwise).\n    \"\"\"\n    max_size = _max_by_axis([list(img.shape) for img in tensor_list])\n    batch_shape = [len(tensor_list)] + max_size\n    b, c, h, w = batch_shape\n    dtype = tensor_list[0].dtype\n    device = tensor_list[0].device\n    tensor = torch.zeros(batch_shape, dtype=dtype, device=device)\n    mask = torch.ones((b, h, w), dtype=torch.bool, device=device)\n    for img, pad_img, m in zip(tensor_list, tensor, mask):\n        pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img)\n        m[: img.shape[1], :img.shape[2]] = False\n    return NestedTensor(tensor, mask)\n\n\ndef nested_tensor_from_tensor_list_visiblility(tensor_list: List[Tensor], size_divisibility=1, split=True):\n    \"\"\"\n    This function receives a list of image tensors and returns a NestedTensor of the padded images, along with their\n    padding masks (true for padding areas, false otherwise).\n    \"\"\"\n    # TODO make this more general\n    # if image tensor is stacked as [T*3, H, W], then use split\n    if split:\n        tensor_list = [tensor.split(3,dim=0) for tensor in tensor_list] \n        tensor_list = [item for sublist in tensor_list for item in sublist] \n        # list[tensor], length = batch_size x time\n\n    if tensor_list[0].ndim == 3: \n        # TODO make it support different-sized images\n        max_size = _max_by_axis([list(img.shape) for img in tensor_list]) \n\n        if size_divisibility > 1: # so that the mask dowmsample can be matched\n            stride = size_divisibility\n            # the last two dims are [H, W], both subject to divisibility requirement\n            max_size[-2] = (max_size[-2] + (stride - 1)) // stride * stride\n            max_size[-1] = (max_size[-1] + (stride - 1)) // stride * stride\n\n        # min_size = tuple(min(s) for s in zip(*[img.shape for img in tensor_list]))\n        batch_shape = [len(tensor_list)] + max_size \n        b, c, h, w = batch_shape \n        dtype = tensor_list[0].dtype\n        device = tensor_list[0].device\n        tensor = torch.zeros(batch_shape, dtype=dtype, device=device) \n        mask = torch.ones((b, h, w), dtype=torch.bool, device=device)\n        for img, pad_img, m in zip(tensor_list, tensor, mask):\n            pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img)\n            m[: img.shape[1], :img.shape[2]] = False # valid locations\n    else:\n        raise ValueError('not supported')\n    return NestedTensor(tensor, mask)\n\ndef nested_tensor_from_tensor_list_with_stride(tensor_list: List[Tensor], max_stride=16):\n    \"\"\"\n    This function receives a list of image tensors and returns a NestedTensor of the padded images, along with their\n    padding masks (true for padding areas, false otherwise).\n    \"\"\"\n    max_size = _max_by_axis([list(img.shape) for img in tensor_list])\n    *_, h,w = max_size\n    if w % max_stride != 0:\n        w += max_stride - (w % max_stride)\n    if h % max_stride != 0:\n        h += max_stride - (h % max_stride)\n    max_size[-1] = w\n    max_size[-2] = h\n    \n    batch_shape = [len(tensor_list)] + max_size\n    b, c, h, w = batch_shape\n    dtype = tensor_list[0].dtype\n    device = tensor_list[0].device\n    tensor = torch.zeros(batch_shape, dtype=dtype, device=device)\n    mask = torch.ones((b, h, w), dtype=torch.bool, device=device)\n    for img, pad_img, m in zip(tensor_list, tensor, mask):\n        pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img)\n        m[: img.shape[1], :img.shape[2]] = False\n    return NestedTensor(tensor, mask)\n\ndef _get_nearest_scale_number(num, scale):\n    res = num % scale\n    if res > 0:\n        return num + (scale - res)\n    else:\n        return num\n\ndef nested_tensor_from_videos_list(videos_list: List[Tensor]):\n    \"\"\"\n    This function receives a list of videos (each of shape [T, C, H, W]) and returns a NestedTensor of the padded\n    videos (shape [T, B, C, PH, PW], along with their padding masks (true for padding areas, false otherwise, of shape\n    [T, B, PH, PW].\n    \"\"\"\n    max_size = _max_by_axis([list(img.shape) for img in videos_list])\n    # max_size[2] = _get_nearest_scale_number(max_size[2], mask_decoder_max_stride)\n    # max_size[3] = _get_nearest_scale_number(max_size[3], mask_decoder_max_stride)\n    padded_batch_shape = [len(videos_list)] + max_size\n    b, t, c, h, w = padded_batch_shape\n    dtype = videos_list[0].dtype\n    device = videos_list[0].device\n    padded_videos = torch.zeros(padded_batch_shape, dtype=dtype, device=device)\n    videos_pad_masks = torch.ones((b, t, h, w), dtype=torch.bool, device=device)\n    for vid_frames, pad_vid_frames, vid_pad_m in zip(videos_list, padded_videos, videos_pad_masks):\n        pad_vid_frames[:vid_frames.shape[0], :, :vid_frames.shape[2], :vid_frames.shape[3]].copy_(vid_frames)\n        vid_pad_m[:vid_frames.shape[0], :vid_frames.shape[2], :vid_frames.shape[3]] = False\n    # transpose the temporal and batch dims and create a NestedTensor:\n    return NestedTensor(padded_videos.transpose(0, 1), videos_pad_masks.transpose(0, 1))\n\n\n# _onnx_nested_tensor_from_tensor_list() is an implementation of\n# nested_tensor_from_tensor_list() that is supported by ONNX tracing.\n@torch.jit.unused\ndef _onnx_nested_tensor_from_tensor_list(tensor_list: List[Tensor]) -> NestedTensor:\n    max_size = []\n    for i in range(tensor_list[0].dim()):\n        max_size_i = torch.max(torch.stack([img.shape[i] for img in tensor_list]).to(torch.float32)).to(torch.int64)\n        max_size.append(max_size_i)\n    max_size = tuple(max_size)\n\n    # work around for\n    # pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img)\n    # m[: img.shape[1], :img.shape[2]] = False\n    # which is not yet supported in onnx\n    padded_imgs = []\n    padded_masks = []\n    for img in tensor_list:\n        padding = [(s1 - s2) for s1, s2 in zip(max_size, tuple(img.shape))]\n        padded_img = torch.nn.functional.pad(img, (0, padding[2], 0, padding[1], 0, padding[0]))\n        padded_imgs.append(padded_img)\n\n        m = torch.zeros_like(img[0], dtype=torch.int, device=img.device)\n        padded_mask = torch.nn.functional.pad(m, (0, padding[2], 0, padding[1]), \"constant\", 1)\n        padded_masks.append(padded_mask.to(torch.bool))\n\n    tensor = torch.stack(padded_imgs)\n    mask = torch.stack(padded_masks)\n\n    return NestedTensor(tensor, mask=mask)\n\n\ndef setup_for_distributed(is_master):\n    \"\"\"\n    This function disables printing when not in master process\n    \"\"\"\n    import builtins as __builtin__\n    builtin_print = __builtin__.print\n\n    def print(*args, **kwargs):\n        force = kwargs.pop('force', False)\n        if is_master or force:\n            builtin_print(*args, **kwargs)\n\n    __builtin__.print = print\n\n\ndef is_dist_avail_and_initialized():\n    if not dist.is_available():\n        return False\n    if not dist.is_initialized():\n        return False\n    return True\n\n\ndef get_world_size():\n    if not is_dist_avail_and_initialized():\n        return 1\n    return dist.get_world_size()\n\n\ndef get_rank():\n    if not is_dist_avail_and_initialized():\n        return 0\n    return dist.get_rank()\n\n\ndef is_main_process():\n    return get_rank() == 0\n\n\ndef save_on_master(*args, **kwargs):\n    if is_main_process():\n        torch.save(*args, **kwargs)\n\n\ndef init_distributed_mode(args):\n    if 'RANK' in os.environ and 'WORLD_SIZE' in os.environ:\n        args.rank = int(os.environ[\"RANK\"])\n        args.world_size = int(os.environ['WORLD_SIZE'])\n        args.gpu = int(os.environ['LOCAL_RANK'])\n    elif 'SLURM_PROCID' in os.environ:\n        args.rank = int(os.environ['SLURM_PROCID'])\n        args.gpu = args.rank % torch.cuda.device_count()\n    else:\n        print('Not using distributed mode')\n        args.distributed = False\n        return\n\n    args.distributed = True\n\n    torch.cuda.set_device(args.gpu)\n    args.dist_backend = 'nccl'\n    print('| distributed init (rank {}): {}'.format(\n        args.rank, args.dist_url), flush=True)\n    torch.distributed.init_process_group(backend=args.dist_backend, init_method=args.dist_url,\n                                         world_size=args.world_size, rank=args.rank)\n    torch.distributed.barrier()\n    setup_for_distributed(args.rank == 0)\n\n\n@torch.no_grad()\ndef accuracy(output, target, topk=(1,)):\n    \"\"\"Computes the precision@k for the specified values of k\"\"\"\n    if target.numel() == 0:\n        return [torch.zeros([], device=output.device)]\n    maxk = max(topk)\n    batch_size = target.size(0)\n\n    _, pred = output.topk(maxk, 1, True, True)\n    pred = pred.t()\n    correct = pred.eq(target.view(1, -1).expand_as(pred))\n\n    res = []\n    for k in topk:\n        correct_k = correct[:k].view(-1).float().sum(0)\n        res.append(correct_k.mul_(100.0 / batch_size))\n    return res\n\n\ndef interpolate(input, size=None, scale_factor=None, mode=\"nearest\", align_corners=None):\n    # type: (Tensor, Optional[List[int]], Optional[float], str, Optional[bool]) -> Tensor\n    \"\"\"\n    Equivalent to nn.functional.interpolate, but with support for empty batch sizes.\n    This will eventually be supported natively by PyTorch, and this\n    class can go away.\n    \"\"\"\n    if float(torchvision.__version__.split(\".\")[1]) < 7.0:\n        if input.numel() > 0:\n            return torch.nn.functional.interpolate(\n                input, size, scale_factor, mode, align_corners\n            )\n\n        output_shape = _output_size(2, input, size, scale_factor)\n        output_shape = list(input.shape[:-2]) + list(output_shape)\n        return _new_empty_tensor(input, output_shape)\n    else:\n        return torchvision.ops.misc.interpolate(input, size, scale_factor, mode, align_corners)\n\ndef inverse_sigmoid(x, eps=1e-5):\n    x = x.clamp(min=0, max=1)\n    x1 = x.clamp(min=eps)\n    x2 = (1 - x).clamp(min=eps)\n    return torch.log(x1/x2)\n\ndef to_device(sample, device):\n    if isinstance(sample, torch.Tensor):\n        sample = sample.to(device)\n    elif isinstance(sample, tuple) or isinstance(sample, list):\n        sample = [to_device(s, device) for s in sample]\n    elif isinstance(sample, dict):\n        sample = {k: to_device(v, device) for k, v in sample.items()}\n    return sample\n\n\ndef nested_tensor_from_videos_list_with_stride(videos_list, max_stride):\n    \"\"\"\n    This function receives a list of videos (each of shape [T, C, H, W]) and returns a NestedTensor of the padded\n    videos (shape [B, T, C, PH, PW], along with their padding masks (true for padding areas, false otherwise, of shape\n    [B, T, PH, PW].\n    \"\"\"\n    temporal_max_stride, spatial_max_stride = max_stride\n    max_size = _max_by_axis([list(video.shape) for video in videos_list]) # list[t 3 h w] -> t b 3 h w\n    t, *_, h,w = max_size\n    if t % temporal_max_stride != 0:\n        t += temporal_max_stride - (t % temporal_max_stride)\n    if w % spatial_max_stride != 0:\n        w += spatial_max_stride - (w % spatial_max_stride)\n    if h % spatial_max_stride != 0:\n        h += spatial_max_stride - (h % spatial_max_stride)\n    max_size[0] = t\n    max_size[2] = h\n    max_size[3] = w\n    \n    padded_batch_shape = [len(videos_list)] + max_size  # b t 3 hp wp\n    b, t, c, h, w = padded_batch_shape\n    dtype = videos_list[0].dtype\n    device = videos_list[0].device\n    padded_videos = torch.zeros(padded_batch_shape, dtype=dtype, device=device)  # b t c hp wp\n    videos_pad_masks = torch.ones((b, t, h, w), dtype=torch.bool, device=device)\n    for vid_frames, pad_vid_frames, vid_pad_m in zip(videos_list, padded_videos, videos_pad_masks):\n        pad_vid_frames[:vid_frames.shape[0], :, :vid_frames.shape[2], :vid_frames.shape[3]].copy_(vid_frames)\n        vid_pad_m[:vid_frames.shape[0], :vid_frames.shape[2], :vid_frames.shape[3]] = False\n    # b t c hp wp\n    return NestedTensor(padded_videos, videos_pad_masks)\n"
  }
]