Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

  • 5b23d0f
  • /
  • src
  • /
  • components
  • /
  • Detection.vue
Raw File Download

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
content badge
swh:1:cnt:b978297c9dc9b8d1b652976d0e0ca93e171dda15
directory badge
swh:1:dir:7acff66984c16996018ed99b98b8a57d53c0cce2

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Detection.vue
<template>
  <v-col cols="9" class="main-view fill-height">
    <info-tooltip
      :left="tooltip.left"
      :top="tooltip.top"
      :width="tooltip.width"
      :show="tooltip.show"
      :content="tooltip.content"
    >
    </info-tooltip>
    <v-dialog v-model="dialog" width="900" height="900" :eager="true">
      <div id="popup-word-tsne" style="width:900px; background-color: white">
        <ul id="tsne-title-row">
          <!-- <div>
                    <span
                    class=""
                    v-text="'Projection of category labels'"
                ></span>
                </div>
                <div>
                    <v-btn
                    text
                    @click="dialog = false"
                >Close
                </v-btn>
                </div> -->

          <li id="tsne-title">Projection of labels</li>
          <li>
            <v-btn text @click="dialog = false" style="font-size: 30px"
              >×
            </v-btn>
          </li>
        </ul>
      </div>
    </v-dialog>
    <v-col cols="12" class="topname fill-width">
      Sample
      <div class="control-panel" style="width: 90%">
        <div class="treecut-control" style="width: 50%">
          <span
            class=""
            v-text="'Treecut: '"
            style="width: 65px; float: left"
          ></span>
          <!-- <input
            class="treecut-radio"
            type="radio"
            id="one"
            value="None"
            v-model="treecut_type"
          /> -->
          <!-- <label class="treecut-option" for="one">None</label> -->
          <input
            class="treecut-radio"
            type="radio"
            id="two"
            value="F1Score"
            v-model="treecut_type"
          />
          <label class="treecut-option" for="two">Correctness</label>
          <input
            class="treecut-radio"
            type="radio"
            id="three"
            value="Mismatch"
            v-model="treecut_type"
          />
          <label class="treecut-option" for="three">Mismatch</label>
        </div>
        <div
          class="consistency-slider"
          id="label-consistency-slider"
          style="width: 45%; display: flex; align-items: center;"
        >
          <span
            class=""
            v-text="'Label consistency weight: ' + label_consistency.toFixed(1)"
            style="width: 65%; float: left; overflow: hidden; text-overflow: ellipsis;"
          ></span>
          <v-slider
            v-model="label_consistency"
            max="100"
            step="1.0"
            :color="'grey'"
            :track-color="'grey lighten-2'"
            :thumb-color="'grey darken-1'"
            style="
                            height: 24px;
                        "
          ></v-slider>
        </div>
        <div
          class="consistency-slider"
          id="symmetrical-consistency-slider"
          style="width: 53%; display: flex; align-items: center;"
        >
          <span
            class=""
            v-text="
              'Symmetrical consistency weight: ' +
                symmetrical_consistency.toFixed(1)
            "
            style="width: 65%; float: left; overflow: hidden; text-overflow: ellipsis;"
          ></span>
          <v-slider
            v-model="symmetrical_consistency"
            max="100"
            :color="'grey'"
            :track-color="'grey lighten-2'"
            :thumb-color="'grey darken-1'"
            style="
                            height: 24px;
                        "
          ></v-slider>
        </div>
        <!-- <div id="label-tsne" @click="onShowLabelTSNECLick()">
          <svg
            t="1625582438485"
            class="icon"
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="2069"
            width="17"
            height="17"
            style="display: block; margin: 3px"
          >
            <circle
              cx="512"
              cy="512"
              r="450"
              style="fill: none; stroke: rgb(114, 114, 114); stroke-width: 95;"
            ></circle>
            <path
              d="M768 256m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z"
              fill="#727272"
              p-id="2070"
            ></path>
            <path
              d="M640 384m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z"
              fill="#727272"
              p-id="2071"
            ></path>
            <path
              d="M352 448m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z"
              fill="#727272"
              p-id="2072"
            ></path>
            <path
              d="M448 288m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z"
              fill="#727272"
              p-id="2073"
            ></path>
            <path
              d="M480 576m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z"
              fill="#727272"
              p-id="2074"
            ></path>
            <path
              d="M288 832m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z"
              fill="#727272"
              p-id="2075"
            ></path>
            <path
              d="M224 608m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z"
              fill="#727272"
              p-id="2076"
            ></path>
            <path
              d="M672 608m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z"
              fill="#727272"
              p-id="2077"
            ></path>
            <path
              d="M544 768m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z"
              fill="#727272"
              p-id="2078"
            ></path>
            <path
              d="M832 512m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z"
              fill="#727272"
              p-id="2079"
            ></path>
            <path
              d="M800 832m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z"
              fill="#727272"
              p-id="2080"
            ></path>
            <path
              d="M192 256m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z"
              fill="#727272"
              p-id="2081"
            ></path>
          </svg>
        </div> -->
        <div id="add-image" @click="onAddImageClick()">
          <svg
            t="1626854048312"
            class="icon"
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="1466"
            width="20"
            height="20"
            style="display: block"
          >
            <path
              style="fill: rgb(114, 114, 114)"
              d="M160 128A96 96 0 0 0 64 224v576A96 96 0 0 0 160 896h262.72a374.464 374.464 0 0 1-25.216-64H160a31.872 31.872 0 0 1-32-32v-59.264l227.52-256.512L430.016 562.56c10.752-19.008 23.232-36.992 37.248-53.504L353.92 389.76 128 644.224V224c0-17.728 14.272-32 32-32h704c17.728 0 32 14.272 32 32v198.72c22.72 11.776 44.48 25.536 64 41.792V224A96 96 0 0 0 864 128zM704 256c-35.2 0-64 28.8-64 64s28.8 64 64 64 64-28.8 64-64-28.8-64-64-64z m32 192C577.28 448 448 577.28 448 736S577.28 1024 736 1024s288-129.28 288-288S894.72 448 736 448z m0 64c124.032 0 224 100.032 224 224 0 124.032-100.032 224-224 224A223.616 223.616 0 0 1 512 736C512 611.968 612.032 512 736 512zM704 576v128H576v64h128v128h64v-128h128v-64h-128V576z"
              fill=""
              p-id="1467"
            ></path>
          </svg>
        </div>
        <div id="update-icon" @click="onUpdateIconCLick()">
          <svg
            t="1625551916203"
            class="icon"
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="2406"
            width="20"
            height="20"
            style="display: block"
          >
            <path
              style="fill: rgb(114, 114, 114)"
              d="M896 432H606.506667l117.12-120.32c-116.48-115.413333-305.066667-119.68-421.546667-4.266667a293.333333 293.333333 0 0 0 0 417.706667c116.48 115.413333 305.066667 115.413333 421.546667 0 58.026667-57.6 87.04-124.373333 86.826666-208.853333H896c0 84.48-37.546667 194.133333-112.64 268.586666-149.76 148.266667-392.96 148.266667-542.72 0s-150.826667-388.693333-1.066667-536.96c149.546667-148.266667 390.186667-148.266667 539.733334 0L896 128v304zM533.333333 341.333333v181.333334l149.333334 88.746666-30.72 51.626667L469.333333 554.666667V341.333333h64z"
              p-id="2407"
            ></path>
          </svg>
        </div>
        <div class="help" id="help-icon" @click="onHelpIconCLick()">
          <div class="question">?</div>
        </div>
      </div>
    </v-col>
    <v-col cols="12" class="main-content pa-0" id="wrapper">
      <div
        id="grid-control"
        style="
                    position: absolute;
                    padding-left: 600px;
                    padding-top: 10px;
                    display: none;
                "
      >
        <div
          id="cropping"
          class="waves-effect waves-light btn-floating grey"
          title="Zoom in"
        >
          <svg
            class="icon"
            width="24px"
            height="24px"
            transform="translate(2.6, 2.6)"
            viewBox="0 0 1024 1024"
          >
            <path
              fill="white"
              d="M136 384h56c4.4 0 8-3.6 8-8V200h176c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H196c-37.6 0-68 30.4-68 68v180c0 4.4 3.6 8 8 8zM648 200h176v176c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V196c0-37.6-30.4-68-68-68H648c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zM376 824H200V648c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v180c0 37.6 30.4 68 68 68h180c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM888 640h-56c-4.4 0-8 3.6-8 8v176H648c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h180c37.6 0 68-30.4 68-68V648c0-4.4-3.6-8-8-8zM904 476H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z"
            />
          </svg>
        </div>
        <!-- <div
          id="selecting"
          style="margin-left: 3px"
          class="waves-effect waves-light btn-floating grey"
          title="Select"
        >
          <svg
            class="icon"
            width="24px"
            height="24px"
            transform="translate(2.6, 2.6)"
            viewBox="0 0 1024 1024"
          >
            <path
              fill="white"
              d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h360c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H184V184h656v320c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V144c0-17.7-14.3-32-32-32zM653.3 599.4l52.2-52.2c4.7-4.7 1.9-12.8-4.7-13.6l-179.4-21c-5.1-0.6-9.5 3.7-8.9 8.9l21 179.4c0.8 6.6 8.9 9.4 13.6 4.7l52.4-52.4 256.2 256.2c3.1 3.1 8.2 3.1 11.3 0l42.4-42.4c3.1-3.1 3.1-8.2 0-11.3L653.3 599.4z"
            />
          </svg>
        </div> -->
        <div
          id="home"
          style="margin-left: 3px"
          class="waves-effect waves-light btn-floating grey"
          title="Home"
        >
          <svg
            class="icon"
            width="24px"
            height="24px"
            transform="translate(2.6, 2.6)"
            viewBox="0 0 1024 1024"
          >
            <path
              fill="white"
              d="M1057.756543 566.281052a44.505707 44.505707 0 0 1-62.953323 0L535.326296 108.094795 150.99726 491.311188h27.927332a89.011415 89.011415 0 0 1 89.011414 89.011415v47.287314l0.244782 268.057876c3.004135 28.817446 13.173689 36.828473 44.950764 39.076011 39.276287-2.892871 43.571088-14.976171 43.971639-64.17723l-0.15577-113.06675a89.011415 89.011415 0 0 1 89.011415-88.165806h44.505707v-1.09039l89.011415-0.133517v1.223907h44.505708a89.011415 89.011415 0 0 1 89.011414 89.011415v47.287314h0.15577v56.010432l0.133517-14.953917c0 69.718191-0.445057 84.560844 44.105156 87.921025 37.451553-2.981882 43.660099-14.442102 44.639225-57.85742l-0.400551 58.346982h0.667585v-73.92398c0 5.629972 0 10.770381-0.178023 15.576998l0.178023-26.102597-0.289287-272.352677a44.505707 44.505707 0 0 1 89.011415 0l0.31154 309.314667h-0.31154V934.988585a89.011415 89.011415 0 0 1-89.011415 89.011415h-89.011415a88.833392 88.833392 0 0 1-88.655369-85.450958L624.159688 823.724317l-0.378298-17.668766c0.15577 2.358802 0.267034 5.029145 0.378298 7.610476L623.981666 756.965756h-24.47814l23.120715 0.15577 1.068137 48.956278c-2.737101-39.165023-13.551988-47.287314-53.896411-48.956278h-73.634693c-37.362541 1.891493-47.376326 10.169554-50.002162 44.928511l0.979125-44.8395h-1.179401v46.441706c0-0.556321 0-1.068137 0.133517-1.602206l-0.133517 5.741236 0.133517 79.798734h-0.133517V934.988585a89.011415 89.011415 0 0 1-89.011415 89.011415h-89.011415a88.833392 88.833392 0 0 1-88.633116-85.183924l-0.378298-359.761886h-18.848168 17.535249l0.400552 59.504131c-1.045884-47.665613-7.855257-57.523627-50.714254-59.548636l-80.288296 0.200275a43.59334 43.59334 0 0 1-11.126427-0.845608l-1.045884-0.200276A40.055137 40.055137 0 0 1 0.901762 544.228474c0-0.289287-0.111264-0.600827-0.15577-0.890114a43.103778 43.103778 0 0 1-0.289287-14.241826 48.956278 48.956278 0 0 1 3.315675-12.305828 44.260926 44.260926 0 0 1 7.944269-11.949783c0.244781-0.244781 0.289287-0.556321 0.534068-0.801103L500.411569 17.258646a47.376326 47.376326 0 0 1 2.781607-4.20579 44.505707 44.505707 0 0 1 62.953323 0l0.645333 0.645333 490.897952 489.562782a44.505707 44.505707 0 0 1 0.066759 63.020081zM713.349126 935.300125h21.140211l-21.140211-0.133517v0.178023z m-445.057074 0h21.117958l-21.117958-0.133517v0.178023zM178.034477 638.714091c0.178023 9.145923 0.200276 19.560258 0.200276 31.710317z"
            />
          </svg>
        </div>
      </div>
    </v-col>
  </v-col>
</template>

<script>
/*
 * this components calls all computation components (treecut, set_managers, etc.)
 * to get elements positions and other attributions,
 * and call all rendering components (text_tree, image_card, etc.)
 * to render all elements.
 */

// import Vue from "vue"
import { mapActions, mapState, mapMutations } from "vuex";
import * as d3 from "d3";
import * as Global from "../plugins/global";
import "../assets/font.css";
import introJs from "intro.js";
import "intro.js/introjs.css";

// computation components
import { mini_tree_layout, TreeCut, tree_layout } from "../plugins/layout_text";
import { image_cluster_list_layout } from "../plugins/layout_image";
import { ConnectionLayout } from "../plugins/layout_connection";
// import { svgDropDown } from "../plugins/svg_drop_down.js";

// import { SetManager } from "../plugins/set_manager";

// render components
import TextTree from "../plugins/render_text_tree";
import TextImageConnection from "../plugins/render_connection";
import ImageCards from "../plugins/render_image_card";

import InfoTooltip from "../components/infotooltip";
export default {
  name: "Detection",
  components: {
    InfoTooltip: InfoTooltip,
  },
  data: () => ({
    dialog: false,
    popup_width: 900,
    // treecut_type: "F1Score",
    treecut_type: "Mismatch",
    items: ["Foo", "Bar", "Fizz", "Buzz"],
    bbox_width: null,
    bbox_height: null,
    layout_width: null,
    layout_height: null,
    top_padding: null,
    nodes: null,
    links: null,
  }),
  computed: {
    ...mapState([
      "word_tsne",
      "classNames",
      "step",
      "name_edit_history",
      "tree",
      "use_treecut",
      "f1_score_selected",
      "image_cluster_list",
      "vis_image_per_cluster",
      "cluster_association_mat",
      "mismatch",
      "all_sets",
      "focus_node",
      "selected_node",
      "expand_tree",
      "expand_set_id",
      "grid_data",
      "grid_image_info",
      "nav_id",
      "tooltip",
      "server_url",
      "selected_flag",
    ]),
    // ...MapGetters({
    //     LabelConsistency: "label_consistency",
    //     SymmetricalSconsistency: "symmetrical_consistency"
    // }),
    label_consistency: {
      get() {
        return this.$store.state.label_consistency;
      },
      set(value) {
        this.$store.state.label_consistency = value;
      },
    },
    symmetrical_consistency: {
      get() {
        return this.$store.state.symmetrical_consistency;
      },
      set(value) {
        this.$store.state.symmetrical_consistency = value;
      },
    },
    // selected_flag(){
    //     return this.tree.all_descendants.map(d => !! d.selected_flag);
    // }
  },
  methods: {
    ...mapActions([
      "fetch_hypergraph",
      "fetch_word",
      "fetch_image",
      "fetch_grid_layout",
      "fetch_single_image_detection_for_focus_text",
    ]),
    ...mapMutations([
      "set_selected_flag",
      "set_focus_node",
      "set_name_edit_history",
      "set_selected_node",
      "set_focus_image",
      "set_expand_tree",
      "set_expand_set_id",
      "showTooltip",
      "hideTooltip",
      "set_words",
      "set_grid_layout_data",
      "set_use_treecut",
      "set_f1_score_selected",
    ]),
    onAddImageClick() {
      console.log("add image");
    },
    onShowLabelTSNECLick() {
      console.log("label-tsne");
      this.dialog = !this.dialog;
    },
    onHelpIconCLick(){
      this.setGuide();
    },
    async onUpdateIconCLick() {
      // console.log("click update icon");
      this.clean();
      window.text.clean();
      window.image.clean();
      Global.begin_loading();
      await this.$store.dispatch("fetch_manifest", {
        step: this.step,
        dataset: "COCO17",
        label_consistency: this.label_consistency,
        symmetrical_consistency: this.symmetrical_consistency,
      });
      await this.$store.dispatch("fetch_hypergraph", 1);
      Global.end_loading();
    },
    clean() {
      this.image_view.clean();
      this.text_tree_view.clean();
      this.connection_view.clean();
    },
    async reproduce(){
      let that = this;
      d3.select("html").attr("style", "overflow-y:hidden");
      await that.set_selected_node({"full_name": "truck", "id": 7});
      await that.fetch_word();
      await that.fetch_single_image_detection_for_focus_text({
          image_id: 100724
        });
      await window.text.set_focus_word({"text": "truck"});
      await window.image.change_confidence();
    },
    async setGuide() {
      let that = this;
      d3.select("html").attr("style", "overflow-y:hidden");
      this.set_selected_node({"full_name": "person", "id": 0});
      this.fetch_word();
      this.fetch_single_image_detection_for_focus_text({
          image_id: 28188
        });
      introJs()
        .onbeforeexit(function() {
          d3.select("html").attr("style", null);
          that.set_selected_node({"full_name": "person", "id": 0});
          window.image.clean();
          window.text.clean(); 
        })
        .setOptions({
          disableInteraction: true,
          steps: [
            {
              element: document.querySelector("#not-to-select"),
              intro:
                "Welcome to MutualDetector. The best experience with a resolution of 1920x1080!",
            },
            {
              element: document.querySelector("#main-svg"),
              intro:
                "This is the set visualization that consists of a tree of labels,  \
                links, and a matrix.",
            },
            {
              element: document.querySelector("#tree-node-group"),
              intro:
                "The tree layout on the left shows labels extracted from captions. \
                The tree can be modified by dragging and dropping if it is not satisfactory.",
            },
            {
              element: document.querySelector("#set-group"),
              intro:
                "The matrix on the right shows objects detected from images.",
            },
            {
              element: document.querySelector("#set-link-group"),
              intro:
                "The links between the tree layout and matrix show the relationships \
                between the labels and image clusters. A link of red dashed line indicates \
                the number of mismatches between an image cluster and a label cluster",
            },
            {
              element: document.querySelector("#id-0"),
              intro:
                "Each rectangle in the tree layout represents one node in the hierarchy. \
                You can click one node to show words with high contributions in the information panel.",
            },
            {
              element: document.querySelector("#set-0"),
              intro:
                "Each row of the matrix represents an image cluster. \
                Several representative images are placed in each row.",
            },
            {
              element: document.querySelector(".expand-rect"),
              intro:
                "Each row of the matrix can be expanded as a grid layout for further exploration of images.\
                You can select images in the grid layout to replace the representative images by the dragging and dropping.",
            },
            {
              element: document.querySelector(".other-view"),
              intro:
                "This is the information panel to show important words, captions, and selected images.",
            },
            {
              element: document.querySelector(".wordcloud-col"),
              intro:
                "Words with high contributions are displayed as a word cloud. \
                You can click one to show captions containing it below.",
            },
            {
              element: document.querySelector("#image-svg"),
              intro:
                "The selected image is displayed here. \
                The green rectangles over the image indicate the positions of detected objects. \
                The rectangle can be modified by dragging the corners.",
            },
            {
              element: document.querySelector(".confidence-slider"),
              intro: "You can change the confidence threshold with the slider.",
            },
            {
              element: document.querySelector(".image-edit"),
              intro: "You can validate, add or remove detected objects.",
            },
          ],
        })
        .start();
    },
    async setGridGuide(){
      let that = this;
      
      introJs()
        .onbeforeexit(function() {
          d3.select("html").attr("style", null);
          that.set_selected_node({"full_name": "person", "id": 0});
          window.image.clean();
          window.text.clean(); 
        })
        .setOptions({
          disableInteraction: true,
          steps: [
            {
              element: document.querySelector("#grid-group"),
              intro:
                "This is the grid layout",
            },
            {
              element: document.querySelector("#cropping"),
              intro:
                "Click this icon and select one region in the grid layout to zoom in",
            },
            {
              element: document.querySelector("#home"),
              intro:
                "Click this icon to to back top level.",
            },
            {
              element: document.querySelector("#no-to-select"),
              intro:
                "Drag one grid to the top to replace the representative images",
            },
          ],
        })
        .start();

    },
    treecut() {
      console.log("detection treecut");
      console.log("before treecut", this.tree);
      if (this.use_treecut) {
        // tree position backup
        this.tree.all_descendants.forEach((d) => {
          d.prev_x = d.x;
          d.prev_y = d.y;
          d.prev_vis = false;
        });
        this.tree.descendants().forEach((d) => (d.prev_vis = true));
        this.offset = this.treecut_class.treeCut(
          this.focus_node,
          this.tree,
          this.tree_layout.layout_with_rest_node
        );
        this.tree.all_descendants.map((d) => (d.api = 0));
        this.offset = 0;
        this.tree.sort(function(a, b) {
          return a.siblings_id - b.siblings_id;
        });
        console.log("after treecut", this.tree);
      } else {
        if (!this.focus_node) {
          this.tree.all_descendants.forEach((d) => (d.children = []));
          this.tree.children = this.tree.all_children;
        } else if (this.focus_node[0].type == 0) {
          this.focus_node[0].children = this.focus_node[0].all_children;
        } else if (this.focus_node[0].type == 1) {
          this.focus_node[0].children = [];
        }
        this.tree.descendants().forEach((d) => {
          d.beforeList = [];
          d.afterList = [];
        });
      }
    },
    update_data() {
      console.log("detection update data");
      console.log(this.tree, this.image_cluster_list);

      // tree layout
      if (!this.use_treecut) {
        this.tree_layout.update_layout_by_num(
          this.tree.descendants().length - 1
        );
      } else {
        this.tree_layout.reset_layout();
      }
      this.nodes = this.tree_layout.layout_with_rest_node(
        this.tree,
        this.expand_tree
      );
      this.rest_nodes = this.nodes.filter((d) => d.is_rest_node);
      this.nodes = this.nodes.filter((d) => !d.is_rest_node);
      this.tree_node_group_x = this.expand_tree ? this.layer_height / 2 : 0;
      this.tree_node_group_y = this.text_height + this.layer_height / 2;
      this.leaf_nodes = this.nodes.filter((d) => d.children.length === 0);
      // this.leaf_nodes.forEach(d => {
      //     if (d.selected_flag===undefined) d.selected_flag = true;
      // });
      // this.selected_nodes = this.nodes.filter(d => d.selected_flag);

      // minitree layout
      let mat = this.mini_tree_layout.layout(this.tree);
      this.mini_nodes = mat.nodes;
      this.mini_links = mat.links;

      // update cut cluster association matrix
      this.connection_layout.update(this.leaf_nodes, this.image_cluster_list);

      // set layout
      console.log("selected_nodes", this.selected_nodes);
      // this.sets = this.connection_layout.reorder(this.image_cluster_list);
      [this.sets, this.grids, this.grid_pos] = this.image_layout.layout(
        this.image_cluster_list
      );

      this.set_links = this.connection_layout.get_links(this.sets);
    },
    update_view() {
      console.log("detection update view");
      let max_height =
        Math.max(...this.nodes.map((d) => d.y)) + this.tree_node_group_y + 20;
      max_height = Math.max(this.bbox_height, max_height);
      if (!this.use_treecut) this.svg.attr("height", max_height);
      else this.svg.attr("height", this.bbox_height);

      this.word_tsne_create();

      this.text_tree_view.sub_component_update(this.nodes, this.rest_nodes);
      this.image_view.sub_component_update(
        this.sets,
        this.vis_image_per_cluster,
        this.grids,
        this.grid_pos
      );
      this.connection_view.sub_component_update(this.set_links);

      this.e_mini_nodes = this.mini_tree_node_group
        .selectAll(".mini-tree-node")
        .data(this.mini_nodes, (d) => d.id);
      this.e_mini_links = this.mini_tree_link_group
        .selectAll(".mini-tree-link")
        .data(this.mini_links);
      this.e_shadow_links = this.mini_shadow_link_group
        .selectAll(".mini-highlight")
        .data(this.mini_links);

      this.remove();
      this.update();
      this.create();
    },
    create() {
      console.log("Global", Global.GrayColor, Global.Animation);
      this.expand_icon_create();
      // this.mini_create();
    },

    mini_create() {
      this.e_mini_nodes
        .enter()
        .append("circle")
        .attr("class", "mini-tree-node")
        .attr("id", (d) => "mini-id-" + d.id)
        .attr("r", 0.5)
        .attr("cx", (d) => d.mini_y)
        .attr("cy", (d) => d.mini_x)
        .style("fill-opacity", 0)
        .transition()
        .duration(this.create_ani)
        .delay(this.remove_ani + this.update_ani)
        .style("fill-opacity", 1);
      this.e_mini_links
        .enter()
        .append("path")
        .attr("class", "mini-tree-link")
        .attr(
          "d",
          d3
            .linkHorizontal()
            .x((d) => d.mini_y)
            .y((d) => d.mini_x)
        )
        .style("opacity", 0)
        .transition()
        .duration(this.create_ani)
        .delay(this.remove_ani + this.update_ani)
        .style("opacity", 1);
      this.e_shadow_links
        .enter()
        .append("path")
        .attr("class", "mini-highlight")
        .attr(
          "d",
          d3
            .linkHorizontal()
            .x((d) => d.mini_y)
            .y((d) => d.mini_x)
        )
        .style("opacity", 0)
        .transition()
        .duration(this.create_ani)
        .delay(this.remove_ani + this.update_ani)
        .style("opacity", (d) => (d.target.mini_selected ? 1 : 0));
    },
    word_tsne_create() {
      d3.select("#popup-word-tsne")
        .select("svg")
        .remove();
      let word_tsne_svg = d3
        .select("#popup-word-tsne")
        .append("svg")
        .style("width", this.popup_width)
        .style("height", this.popup_width);
      // word_tsne_svg.append("rect")
      //     .style("width", this.popup_width)
      //     .style("height", this.popup_width)
      //     .style("fill", "white");
      this.e_word_tsne = word_tsne_svg
        .selectAll("circle.word-point")
        .data(() => {
          let res = [];
          for (let i = 0; i < this.word_tsne.length; i++) {
            let p = {};
            p.name = this.classNames[i];
            p.x =
              this.word_tsne[i][0] * this.popup_width * 0.9 +
              this.popup_width * 0.05;
            p.y =
              this.word_tsne[i][1] * this.popup_width * 0.9 +
              this.popup_width * 0.05;
            if (i === 9) {
              p.x = p.x * 1.05;
              p.y = p.y * 1.1;
            }
            if (i === 40) {
              p.y = p.y * 0.9;
            }
            if (i === 30) {
              p.y = p.y * 0.95;
            }
            res.push(p);
          }
          return res;
        });
      this.e_word_tsne
        .enter()
        .append("circle")
        .attr("class", "word-point")
        .attr("cx", (d) => d.x)
        .attr("cy", (d) => d.y)
        .attr("r", 5)
        .style("fill", "#727272");
      this.e_word_tsne
        .enter()
        .append("text")
        .attr("x", (d) => d.x + 10)
        .attr("y", (d) => d.y + 5)
        .style("fill", "#727272")
        .text((d) => d.name);
    },
    legend_create() {
      let that = this;

      let title1_x = 20;
      let title2_x = 350;
      let title2_len = 100;

      let top_y = 0;

      // titles
      that.svg
        .append("text")
        .attr("class", "title-text")
        .attr("x", title1_x)
        .attr("y", that.text_height * 0.6 + "px")
        .text("Label hierarchy");
      //   let label_tsne_svg = that.svg
      //     .append("svg")
      //     .attr("id", "label-tsne")
      //     .attr("viewBox", "0 0 1024 1024")
      //     .attr("x", title1_x + 130)
      //     .attr("y", that.text_height * 0.2 + "px")
      //     .attr("width", "20px")
      //     .attr("height", "20px");
      //   let paths = [
      //     "M640 384m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z",
      //     "M352 448m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z",
      //     "M448 288m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z",
      //     "M480 576m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z",
      //     "M288 832m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z",
      //     "M224 608m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z",
      //     "M672 608m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z",
      //     "M544 768m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z",
      //     "M832 512m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z",
      //     "M800 832m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z",
      //     "M192 256m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z",
      //   ];
      //   label_tsne_svg
      //     .append("rect")
      //     .attr("width", 1024)
      //     .attr("height", 1024)
      //     .style("fill", "white");
      //   label_tsne_svg
      //     .append("circle")
      //     .attr("cx", 512)
      //     .attr("cy", 512)
      //     .attr("r", 450)
      //     .style("fill", "none")
      //     .style("stroke", "#727272")
      //     .style("stroke-width", 95);
      //   label_tsne_svg
      //     .selectAll("path.tsne-point")
      //     .data(paths)
      //     .enter()
      //     .append("path")
      //     .attr("class", "tsne-point")
      //     .attr("d", (d) => d)
      //     .style("fill", "#727272");
      //   label_tsne_svg
      //     .on("mouseover", () => {
      //       d3.select("#label-tsne")
      //         .select("rect")
      //         .style("fill", "#ddd");
      //     })
      //     .on("mouseout", () => {
      //       d3.select("#label-tsne")
      //         .select("rect")
      //         .style("fill", "white");
      //     })
      //     .on("click", () => {
      //       console.log("label-tsne");
      //       this.dialog = !this.dialog;
      //     });

      that.svg
        .append("text")
        .attr("class", "title-text")
        .attr("x", title2_x)
        .attr("y", that.text_height * 0.6 + "px")
        .text("Image cluster");

      // precision & recall legend
      let precision_color = "rgb(201, 130, 206)";
      let recall_color = "rgb(79, 167, 255)";
      let rect_size = 0.166 * that.text_height;
      let step = 0.55 * that.text_height;
      let precision_recall_legend_startx = title2_x + title2_len + 100;
      let pc_group = that.svg
        .append("g")
        .attr("class", "precision-recall-legend")
        .attr(
          "transform",
          "translate(" +
            precision_recall_legend_startx +
            "," +
            top_y +
            ")" +
            "scale(" +
            1 +
            "," +
            1 +
            ")"
        );
      pc_group
        .append("rect")
        .attr("x", 0)
        .attr("y", rect_size)
        // .attr("y", rect_size+10)
        .attr("rx", 1.5)
        .attr("ry", 1.5)
        .attr("width", rect_size)
        .attr("height", rect_size)
        .attr("stroke-width", 1)
        .attr("stroke", precision_color)
        .attr("fill", precision_color);
      pc_group
        .append("rect")
        .attr("x", 0)
        .attr("y", 0)
        // .attr("y", 10)
        .attr("rx", 1.5)
        .attr("ry", 1.5)
        .attr("width", rect_size)
        .attr("height", rect_size * 2)
        .attr("stroke-width", 1)
        .attr("stroke", precision_color)
        .attr("fill", "none");

      pc_group
        .append("rect")
        // .attr("x", rect_size+93)
        .attr("x", 0)
        .attr("y", rect_size + step)
        // .attr("y", rect_size+10)
        .attr("rx", 1.5)
        .attr("ry", 1.5)
        .attr("width", rect_size)
        .attr("height", rect_size)
        .attr("stroke-width", 1)
        .attr("stroke", recall_color)
        .attr("fill", recall_color);
      pc_group
        .append("rect")
        // .attr("x", rect_size+93)
        .attr("x", 0)
        .attr("y", step)
        // .attr("y", 10)
        .attr("rx", 1.5)
        .attr("ry", 1.5)
        .attr("width", rect_size)
        .attr("height", rect_size * 2)
        .attr("stroke-width", 1)
        .attr("stroke", recall_color)
        .attr("fill", "none");

      pc_group
        .append("text")
        .attr("text-anchor", "start")
        .attr("x", rect_size + 5)
        .attr("y", 0)
        .style("dominant-baseline", "hanging")
        .text("Precision");

      pc_group
        .append("text")
        .attr("text-anchor", "start")
        .attr("x", rect_size + 5)
        .attr("y", step)
        .style("dominant-baseline", "hanging")
        .text("Recall");

      // match & mismatch line legend
      let match_color = "#D3D3E5";
      let mismatch_color = "#ED2939";
      let line_stroke = 1;
      let line_length = 30;
      let match_mismatch_legend_startx =
        precision_recall_legend_startx + 60 + 50;
      let match_group = that.svg
        .append("g")
        .attr("id", "match-mismatch-legend-group")
        .attr(
          "transform",
          "translate(" +
            match_mismatch_legend_startx +
            "," +
            top_y +
            ")" +
            "scale(" +
            1 +
            "," +
            1 +
            ")"
        );
      match_group
        .selectAll(".match-mismatch-legend")
        .data(["Mismatched cluster pair", "Matched cluster pair"])
        .enter()
        .append("g")
        .attr("class", "match-mismatch-legend")
        .each(function(d, i) {
          let group = d3.select(this);
          group
            .append("line")
            .attr("x1", 0)
            .attr("y1", step * i + 7.5)
            .attr("x2", line_length)
            .attr("y2", step * i + 7.5)
            .attr("stroke-dasharray", i === 0 ? "5,5" : "5,0")
            .style("stroke-width", line_stroke)
            .style("stroke", i === 0 ? mismatch_color : match_color);
          group
            .append("text")
            .attr("x", 40)
            .attr("y", step * i)
            .text(d)
            .style("dominant-baseline", "hanging");
        });
      // match_group.append("line")
      //     .attr("x1", 0)
      //     .attr("y1", 15)
      //     .attr("x2", line_length)
      //     .attr("y2", 15)
      //     .attr("stroke-width", line_stroke)
      //     .attr("stroke", match_color);

      // match_group.append("line")
      //     .attr("x1", 220)
      //     .attr("y1", 15)
      //     .attr("x2", line_length+220)
      //     .attr("y2", 15)
      //     .attr("stroke-dasharray", "5,5")
      //     .attr("stroke-width", line_stroke)
      //     .attr("stroke", mismatch_color);

      // match_group.append("text")
      //     .attr("text-anchor", "start")
      //     .attr("x", 3+line_length)
      //     .attr("y", 22)
      //     .attr("font-size", "18px")
      //     .text("Matched cluster pair");

      // match_group.append("text")
      //     .attr("text-anchor", "start")
      //     .attr("x", line_length+220)
      //     .attr("y", 22)
      //     .attr("font-size", "18px")
      //     .text("Mismatched cluster pair");

      // 4. Grid_layout legend and buttons
      let grid_legend_startx = match_mismatch_legend_startx + 200 + 50;
      let grid_legend_group = this.svg
        .append("g")
        .attr("id", "grid-legend-group")
        .attr(
          "transform",
          "translate(" +
            grid_legend_startx +
            "," +
            top_y +
            ")" +
            "scale(" +
            1 +
            "," +
            1 +
            ")"
        )
        .style("visibility", "hidden");
      grid_legend_group
        .selectAll(".grid-legend")
        .data(["Mismatched Samples", "Matched Samples"])
        .enter()
        .append("g")
        .attr("class", "grid-legend")
        .each(function(d, i) {
          let group = d3.select(this);
          group
            .append("rect")
            .attr("x", 0)
            .attr("y", 20 * i)
            .attr("width", 15)
            .attr("height", 15)
            .style("fill", i === 0 ? "#E05246" : "#EEEDF3");
          group
            .append("text")
            .attr("x", 20)
            .attr("y", 20 * i)
            .text(d)
            .style("dominant-baseline", "hanging");
        });
      d3.select("#grid-control").style(
        "padding-left",
        grid_legend_startx + 200 + "px"
      );
    },
    expand_icon_create() {
      // this.expanded_icon_group.on("click", () => {
      //     console.log("click expanded icon", this.expand_tree);
      //     this.set_expand_tree(!this.expand_tree);
      // });
      // this.expanded_icon_group
      //     .selectAll("rect")
      //     .data([this.expand_tree])
      //     .enter()
      //     .append("rect")
      //     .attr("width", 10)
      //     .attr("height", 10)
      //     .style("rx", 3)
      //     .style("ry", 3)
      //     .style("fill", "white")
      //     .style("stroke", "gray")
      //     .style("stroke-width", 1);
      // this.expanded_icon_group
      //     .selectAll("path")
      //     .data([this.expand_tree])
      //     .enter()
      //     .append("path")
      //     .style("stroke", "none")
      //     .style("fill", "gray")
      //     .attr("d", () => {
      //         if (this.expand_tree) {
      //             return Global.minus_path_d(0, 0, 10, 10, 2);
      //         } else {
      //             return Global.plus_path_d(0, 0, 10, 10, 2);
      //         }
      //     });
    },
    update() {
      this.expand_icon_update();
      // this.mini_update();
    },
    mini_update() {
      this.e_mini_nodes
        .transition()
        .duration(this.update_ani)
        .delay(this.remove_ani)
        .attr("cx", (d) => d.mini_y)
        .attr("cy", (d) => d.mini_x);
      this.e_mini_links
        .transition()
        .duration(this.update_ani)
        .delay(this.remove_ani)
        .attr(
          "d",
          d3
            .linkHorizontal()
            .x((d) => d.mini_y)
            .y((d) => d.mini_x)
        );
      this.e_shadow_links
        .transition()
        .duration((d) =>
          d.target.mini_selected ? this.create_ani : this.update_ani
        )
        .delay((d) =>
          d.target.mini_selected
            ? this.update_ani + this.remove_ani
            : this.remove_ani
        )
        .attr(
          "d",
          d3
            .linkHorizontal()
            .x((d) => d.mini_y)
            .y((d) => d.mini_x)
        )
        .style("opacity", (d) => (d.target.mini_selected ? 1 : 0));
    },
    expand_icon_update() {
      // this.expanded_icon_group
      //     .selectAll("path")
      //     .data([this.expand_tree])
      //     .attr("d", () => {
      //         if (this.expand_tree) {
      //             return Global.minus_path_d(0, 0, 10, 10, 2);
      //         } else {
      //             return Global.plus_path_d(0, 0, 10, 10, 2);
      //         }
      //     });
    },
    remove() {
      // this.mini_remove();
    },
    mini_remove() {},
  },
  watch: {
    treecut_type() {
      // console.log("checkbox", this.picked);
      if (this.treecut_type === "None") {
        console.log("click tree cut", this.use_treecut);
        this.set_use_treecut(false);
      } else if (this.treecut_type === "F1Score") {
        console.log("click prec-rec-checkbox", this.f1_score_selected);
        if (!this.f1_score_selected) {
          this.set_use_treecut(true);
          // this.use_treecut = true;
          console.log(
            "use_treecut, f1 score",
            this.use_treecut,
            this.f1_score_selected
          );
          this.set_f1_score_selected(true);
        }
      } else if (this.treecut_type === "Mismatch") {
        console.log("click prec-rec-checkbox", this.f1_score_selected);
        if (this.f1_score_selected) {
          this.set_use_treecut(true);
          // this.use_treecut = true;
          this.set_f1_score_selected(false);
        }
      } else {
        console.log("ERROR: no option named", this.treecut_type);
      }
    },
    f1_score_selected() {
      console.log("f1_score_selected", this.f1_score_selected);
      this.tree.all_descendants.forEach((d) => {
        d.api = this.f1_score_selected ? d.f1_api : d.mm_api;
      });
      console.log("api", this.tree.all_descendants.map(d => d.api));
      this.treecut();
      console.log("offset", this.offset);
      this.update_data();
      this.update_view();
    },
    // name_edit_history: {
    //     handler (_, oldValue) {
    //         if (oldValue === 0) return;
    //         console.log("name_edit_history update");
    //         this.treecut();
    //         console.log("offset", this.offset);
    //         this.update_data();
    //         this.update_view();
    //     },
    //     deep: true
    // },
    tree() {
      console.log("tree update");
      this.treecut();
      console.log("offset", this.offset);
      this.update_data();
      this.update_view();
    },
    use_treecut() {
      console.log("use_treecut");
      this.treecut();
      console.log("offset", this.offset);
      this.update_data();
      this.update_view();
    },
    selected_flag() {
      console.log("selected flag update");
      this.update_data();
      this.update_view();
    },
    focus_node() {
      console.log("focus_node change", this.focus_node);
      this.treecut();
      console.log("offset", this.offset);
      this.update_data();
      this.update_view();
    },
    expand_tree() {
      console.log("expand tree change", this.expand_tree);
      // this.treecut(); // TODO:
      this.update_data();
      this.update_view();
    },
    async expand_set_id() {
      console.log("watch expand set id");
      if (this.expand_set_id < 0) {
        this.update_data();
        this.update_view();
      } else {
        let selected_ids = this.selected_node.node_ids;
        let image_cluster_id = this.expand_set_id;
        this.update_data();
        this.grids = [];
        this.grid_pos = {};
        this.image_view.create_ani = 0;
        this.update_view();
        this.image_view.set_animation_time();
        Global.disable_global_interaction();
        d3.select(".loading-svg").style("display", "block");
        setTimeout(() => {
          this.fetch_grid_layout({
            cat_ids: selected_ids,
            image_cluster_id: image_cluster_id,
          });
        }, 1000);
      }
    },
    grid_data() {
      console.log("watch grid_data");
      this.update_data();
      this.update_view();
      d3.select(".loading")
        .transition()
        .duration(1)
        .delay(1)
        .style("display", "none")
        .style("opacity", 1);
      d3.select(".loading-svg")
        .transition()
        .duration(1)
        .delay(1)
        .style("display", "none");
    },
  },
  async mounted() {
    console.log("detection mounted");
    window.detection = this;
    window.d3 = d3;
    let container = d3.select(".main-content");
    let bbox = container.node().getBoundingClientRect();
    this.bbox_width = bbox.width;
    this.bbox_height = bbox.height;

    // text position
    this.text_height = this.bbox_height * 0.04;

    // node width
    this.max_text_width = 120; // fixed max_text_width

    // mini tree
    this.mini_tree_width = 35;
    this.mini_tree_height = 80;
    this.mini_tree_x = 120;
    this.mini_tree_y = 5;

    // detection result layout
    this.layout_width = this.bbox_width;
    this.layout_height = this.bbox_height - this.text_height;
    this.node_width = 20; // TODO
    this.layer_height = 40; // TODO

    // bar size
    this.bar_width = 6;
    this.bar_height = this.layer_height * 0.45;
    this.rounded_r = 1.5;

    // set
    this.set_num = 0;
    this.set_height = 0;
    this.image_height = 0;
    this.set_left = this.layer_height * 3 + 230;
    this.set_width = this.layout_width - this.set_left - 12;
    this.set_margin = 3;
    this.image_margin = 5;

    // animation
    this.create_ani = Global.Animation;
    this.update_ani = Global.Animation;
    this.remove_ani = Global.Animation / 2;

    this.svg = container
      .append("svg")
      .attr("id", "main-svg")
      .attr("width", this.bbox_width)
      .attr("height", this.bbox_height)
      .style("padding-top", "5px");

      
    this.expanded_icon_group = this.svg
      .append("g")
      .attr("id", "expanded-icon-group")
      .attr(
        "transform",
        "translate(" + 5 + ", " + this.text_height * 0.8 + ")"
      );
    this.rest_node_group = this.svg
      .append("g")
      .attr("id", "rest-node-group")
      .attr(
        "transform",
        "translate(" + 2 + ", " + this.layout_height / 2 + ")"
      );
    this.tree_node_group = this.svg
      .append("g")
      .attr("id", "tree-node-group")
      .attr(
        "transform",
        "translate(" + 2 + ", " + this.layout_height / 2 + ")"
      );
    this.mini_tree_node_group = this.svg
      .append("g")
      .attr("id", "mini-tree-node-group")
      .attr(
        "transform",
        "translate(" + this.mini_tree_x + ", " + this.mini_tree_y + ")"
      );
    this.mini_tree_link_group = this.svg
      .append("g")
      .attr("id", "mini-tree-link-group")
      .attr(
        "transform",
        "translate(" + this.mini_tree_x + ", " + this.mini_tree_y + ")"
      );
    this.mini_shadow_link_group = this.svg
      .append("g")
      .attr("id", "mini-shadow-link-group")
      .attr(
        "transform",
        "translate(" + this.mini_tree_x + ", " + this.mini_tree_y + ")"
      );
    this.set_group = this.svg
      .append("g")
      .attr("id", "set-group")
      .attr("transform", "translate(" + 0 + ", " + this.text_height + ")");
    this.grid_group = this.svg
      .append("g")
      .attr("id", "grid-group")
      .attr("transform", "translate(" + 0 + ", " + this.text_height + ")");
    this.label_group = this.svg
      .append("g")
      .attr("id", "label-group")
      .attr("transform", "translate(" + 0 + ", " + this.text_height + ")");
    this.nav_group = this.svg
      .append("g")
      .attr("id", "nav-group")
      .attr("transform", "translate(" + 0 + ", " + 0 + ")");
    this.nav_group.style("visibility", "hidden");
    this.set_link_group = this.svg
      .append("g")
      .attr("id", "set-link-group")
      .attr("transform", "translate(" + 0 + ", " + this.text_height + ")");
    this.legend_create();
    this.drag_grid_group = this.svg
      .append("g")
      .attr("id", "drag-grid-group")
      .attr("transform", "translate(" + 0 + ", " + this.text_height + ")");
    this.tree_layout = new tree_layout(
      [this.node_width, this.layer_height],
      this.layout_height
    );

    this.mini_tree_layout = new mini_tree_layout([
      this.mini_tree_width,
      this.mini_tree_height,
    ]);

    this.treecut_class = new TreeCut(
      this.layer_height * 5,
      this.layout_height,
      this.layer_height
    );

    this.image_layout = new image_cluster_list_layout(this);
    this.connection_layout = new ConnectionLayout(this);
    // this.set_manager = new SetManager(this);

    this.text_tree_view = new TextTree(this);
    this.connection_view = new TextImageConnection(this);
    this.image_view = new ImageCards(this);
  },
};
</script>

<style>
/* .tree-node{
} */
html {
  font-size: 0.835vw;
}

html::-webkit-scrollbar {
  display: none;
}

.icon-bg-0 {
  cursor: pointer;
}

.node-name {
  pointer-events: none;
}

.rest-tree-node {
  cursor: pointer;
}

.tree-link {
  fill: none;
}

.set-link {
  fill: none;
}

.mini-tree-node {
  fill: #dfdfdf;
}

.bar-background {
  fill: white;
  stroke: rgb(127, 127, 127);
}

.bar-line {
  fill: rgb(127, 127, 127);
}

.bar-precision {
  fill: rgb(201, 130, 206);
}

.bar-recall {
  fill: rgb(79, 167, 255);
}

.mini-tree-link {
  stroke: #dfdfdf;
  fill: none;
}

#overlay {
  position: absolute;
  display: flex;
  flex-direction: column;
  /* padding: 0.7em; */
  max-width: 200px;
  min-width: 100px;
  /* background: rgb(255, 255, 255);
    color:rgb(114, 114, 114);
    border-radius: 3px; */
  text-align: center;
  background-color: rgba(255, 255, 255, 0);
  /* box-shadow: 0 4px 8px 0 rgb(0 0 0 / 40%), 0 6px 20px 0 rgb(0 0 0 / 29%); */
}

#edit-title {
  font-size: 18px;
  font-weight: 600;
}

#edit-input {
  /* text-align: center; */
  color: rgb(114, 114, 114);
  font-size: 18px;
  height: 24px;
  background: white;
  border-style: solid;
  border: 1px solid #e0e0e0;
  /* margin-top: 0.7em; */
  /* margin-bottom: 0.7em; */
}

#edit-input:focus {
  /* border-bottom:1px solid #4286bd; */
  outline: none;
  border-bottom: 1px solid #4286bd;
}

/* .mini-shadow-link{
    stroke:
} */

.mini-highlight {
  stroke: #5f5f5f;
  fill: none;
}

.title-text {
  font-size: 18px;
  stroke: rgb(114, 114, 114);
  fill: rgb(114, 114, 114);
}

.control-panel {
  /* justify-content: flex-end; */
  display: flex;
  align-items: center;
  white-space: nowrap;
  text-overflow: ellipsis;
  /* font-size: 16px; */
  font-size: 0.835vw;
  font-weight: 400;
}

.treecut-control {
  display: flex;
  align-items: center;
  margin-right: 20px;
}

.treecut-option {
  margin-right: 10px;
}

.treecut-radio {
  margin-right: 2px;
  margin-bottom: 3px;
}

.help .question {
  height: 20px;
  width: 20px;
  background: #ccc;
  font-size: 16px;
  line-height: 20px;
  text-align: center;
  border-radius: 50%;
  cursor: pointer;
}

#help-icon:hover {
  background: #ddd;
}

.v-slider--horizontal {
  min-height: 22px;
}

.main-content {
  /* background: rgb(248, 249, 254); */
  background: rgb(255, 255, 255);
  border: 1px solid #c1c1c1;
  border-radius: 5px;
  height: calc(100% - 24px);
}

.precision-recall-legend {
  fill: rgb(114, 114, 114);
}

#match-mismatch-legend-group {
  fill: rgb(114, 114, 114);
}

#grid-legend-group {
  fill: rgb(114, 114, 114);
}

#update-icon:hover {
  background: #ddd;
}

#label-tsne:hover {
  background: #ddd;
}

#add-image:hover {
  background: #ddd;
}

#popup-word-tsne {
  display: flex;
  flex-direction: column;
  /* justify-content: flex-end; */
  /* align-items: center; */
}

#tsne-title {
  font-size: 20px;
  font-weight: 600;
  color: rgb(114, 114, 114);
}

ul::before {
  content: "D";
  margin: 1px auto 1px 1px;
  visibility: hidden;
}
li:last-child {
  margin-left: auto;
}
ul {
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}
li {
  display: flex;
  margin: 1px;
}
p {
  text-align: center;
  margin-top: 0;
}

input[type="radio"] {
  /* remove standard background appearance */
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  /* create custom radiobutton appearance */
  display: inline-block;
  width: 14px;
  height: 14px;
  padding: 2.4px;
  /* background-color only for content */
  background-clip: content-box;
  border: 1px solid #494949;
  /* background-color: #ffffff; */
  border-radius: 50%;
}

/* appearance for checked radiobutton */
input[type="radio"]:checked {
  background-color: #050505;
}

.topname {
  display: flex;
  align-items: center;
  font-size: 20px;
  font-family: "Roboto", "Helvetica", "Arial", sans-serif;
  font-weight: 600;
  background: rgb(238, 240, 240);
  border-radius: 5px;
  padding-left: 10px;
  color: rgb(114, 114, 114);
  height: 22px;
  justify-content: space-between;
}

#main-topname {
  display: flex;
  justify-content: space-between;
}

.matched-link {
  stroke: #d3d3e5;
}

.mismatched-link {
  stroke: #ed2939;
  stroke-dasharray: 5, 5;
}

.current-label-checkbox {
  cursor: pointer;
}
.prec-rec-checkbox {
  cursor: pointer;
}
.mismatch-checkbox {
  cursor: pointer;
}

.expand-path {
  pointer-events: none;
}

.waves-effect {
  position: relative;
  cursor: pointer;
  display: inline-block;
  overflow: hidden;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  vertical-align: middle;
  z-index: 1;
  -webkit-transition: 0.3s ease-out;
  transition: 0.3s ease-out;
}

.waves-effect .waves-ripple {
  position: absolute;
  border-radius: 50%;
  width: 20px;
  height: 20px;
  margin-top: -10px;
  margin-left: -10px;
  opacity: 0;
  background: rgba(0, 0, 0, 0.2);
  -webkit-transition: all 0.7s ease-out;
  transition: all 0.7s ease-out;
  -webkit-transition-property: opacity, -webkit-transform;
  transition-property: opacity, -webkit-transform;
  transition-property: transform, opacity;
  transition-property: transform, opacity, -webkit-transform;
  -webkit-transform: scale(0);
  transform: scale(0);
  pointer-events: none;
}

.waves-effect.waves-light .waves-ripple {
  background-color: rgba(255, 255, 255, 0.45);
}

.waves-effect.waves-red .waves-ripple {
  background-color: rgba(244, 67, 54, 0.7);
}

.waves-effect.waves-yellow .waves-ripple {
  background-color: rgba(255, 235, 59, 0.7);
}

.waves-effect.waves-orange .waves-ripple {
  background-color: rgba(255, 152, 0, 0.7);
}

.waves-effect.waves-purple .waves-ripple {
  background-color: rgba(156, 39, 176, 0.7);
}

.waves-effect.waves-green .waves-ripple {
  background-color: rgba(76, 175, 80, 0.7);
}

.waves-effect.waves-teal .waves-ripple {
  background-color: rgba(0, 150, 136, 0.7);
}

.waves-effect input[type="button"],
.waves-effect input[type="reset"],
.waves-effect input[type="submit"] {
  border: 0;
  font-style: normal;
  font-size: inherit;
  text-transform: inherit;
  background: none;
}

.waves-effect img {
  position: relative;
  z-index: -1;
}

.waves-notransition {
  -webkit-transition: none !important;
  transition: none !important;
}

.waves-circle {
  -webkit-transform: translateZ(0);
  transform: translateZ(0);
  -webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%);
}

.waves-input-wrapper {
  border-radius: 0.2em;
  vertical-align: bottom;
}

.waves-input-wrapper .waves-button-input {
  position: relative;
  top: 0;
  left: 0;
  z-index: 1;
}

.waves-circle {
  text-align: center;
  width: 2.5em;
  height: 2.5em;
  line-height: 2.5em;
  border-radius: 50%;
  -webkit-mask-image: none;
}

.waves-block {
  display: block;
}

.waves-effect .waves-ripple {
  z-index: -1;
}

.btn-floating {
  display: inline-block;
  color: #fff;
  position: relative;
  overflow: hidden;
  z-index: 1;
  width: 30px;
  height: 30px;
  line-height: 30px;
  padding: 0;
  background-color: #26a69a;
  border-radius: 50%;
  -webkit-transition: background-color 0.3s;
  transition: background-color 0.3s;
  cursor: pointer;
  vertical-align: middle;
}

.btn-floating:hover {
  background-color: #26a69a;
}

.btn-floating:before {
  border-radius: 0;
}

.grey {
  background-color: #9e9e9e !important;
}

.glyphicon {
  position: relative;
  top: 1px;
  display: inline-block;
  font-family: "Glyphicons Halflings";
  font-style: normal;
  font-weight: normal;
  line-height: 1;

  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.glyphicon-zoom-in:before {
  content: "\e015";
}
</style>

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API