From 6ed0251b38ca8c52bfb1b89e4ab9bbcd0d390b4e Mon Sep 17 00:00:00 2001 From: guhaomine <18380425155@163.com> Date: Thu, 19 Oct 2023 10:49:33 +0800 Subject: [PATCH] feat:scene Signed-off-by: guhaomine <18380425155@163.com> --- .../pc-tool/public/iconfont/demo_index.html | 3910 +++++++++++------ frontend/pc-tool/public/iconfont/iconfont.css | 230 +- frontend/pc-tool/public/iconfont/iconfont.js | 66 +- .../pc-tool/public/iconfont/iconfont.json | 394 +- frontend/pc-tool/public/iconfont/iconfont.ttf | Bin 16560 -> 35024 bytes .../pc-tool/public/iconfont/iconfont.woff | Bin 11392 -> 22580 bytes .../pc-tool/public/iconfont/iconfont.woff2 | Bin 9764 -> 19256 bytes frontend/pc-tool/src/common/DataManager.ts | 6 +- .../src/components/EditClass/useEditClass.ts | 15 +- .../pc-tool/src/components/Editor/main.vue | 7 + .../pc-tool/src/components/Header/index.vue | 2 +- .../pc-tool/src/components/Instance/index.vue | 94 +- .../pc-tool/src/components/Instance/type.ts | 1 + .../src/components/Instance/useClassItem.ts | 11 +- .../src/components/Instance/useInstance.ts | 36 +- .../src/components/Operation/index.vue | 2 + .../pc-tool/src/components/TimeLine/index.vue | 276 ++ .../src/components/TimeLine/tickLine.vue | 411 ++ .../src/components/TimeLine/toolbar.vue | 463 ++ .../src/components/TimeLine/trackLine.vue | 381 ++ .../src/components/TimeLine/useTimeLine.ts | 616 +++ frontend/pc-tool/src/config/action.ts | 2 + frontend/pc-tool/src/hook/useTool.ts | 6 +- .../pc-tool/src/packages/pc-editor/Editor.ts | 40 +- .../common/ActionManager/action/create.ts | 34 +- .../common/ActionManager/action/create2D.ts | 85 +- .../common/ActionManager/action/general.ts | 6 +- .../common/CmdManager/cmd/AddTrack.ts | 30 + .../common/CmdManager/cmd/DeleteTrack.ts | 37 + .../common/CmdManager/cmd/UpdateTrackData.ts | 49 + .../CmdManager/cmd/UpdateTrackDataBatch.ts | 91 + .../CmdManager/cmd/UpdateTransformBatch.ts | 41 + .../pc-editor/common/CmdManager/cmd/index.ts | 19 +- .../packages/pc-editor/common/DataManager.ts | 172 +- .../packages/pc-editor/common/DataResource.ts | 1 + .../packages/pc-editor/common/LoadManager.ts | 67 +- .../packages/pc-editor/common/PlayManager.ts | 87 + .../packages/pc-editor/common/TrackManager.ts | 498 +++ .../src/packages/pc-editor/config/event.ts | 7 +- .../src/packages/pc-editor/config/hotkey.ts | 2 + .../pc-tool/src/packages/pc-editor/lang/en.ts | 24 +- .../pc-tool/src/packages/pc-editor/lang/zh.ts | 23 +- .../pc-tool/src/packages/pc-editor/state.ts | 8 +- .../pc-tool/src/packages/pc-editor/type.ts | 26 +- .../src/packages/pc-editor/utils/data.ts | 188 +- .../src/packages/pc-editor/utils/point.ts | 72 +- .../src/packages/pc-editor/utils/track.ts | 1 + .../src/packages/pc-render/PointCloud.ts | 5 +- .../src/packages/pc-render/objects/Box.ts | 2 + .../packages/pc-render/objects/object2d.ts | 4 + frontend/pc-tool/src/pages/execute.ts | 4 + frontend/pc-tool/vite.config.ts | 3 +- 52 files changed, 6947 insertions(+), 1608 deletions(-) create mode 100644 frontend/pc-tool/src/components/TimeLine/index.vue create mode 100644 frontend/pc-tool/src/components/TimeLine/tickLine.vue create mode 100644 frontend/pc-tool/src/components/TimeLine/toolbar.vue create mode 100644 frontend/pc-tool/src/components/TimeLine/trackLine.vue create mode 100644 frontend/pc-tool/src/components/TimeLine/useTimeLine.ts create mode 100644 frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/AddTrack.ts create mode 100644 frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/DeleteTrack.ts create mode 100644 frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/UpdateTrackData.ts create mode 100644 frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/UpdateTrackDataBatch.ts create mode 100644 frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/UpdateTransformBatch.ts create mode 100644 frontend/pc-tool/src/packages/pc-editor/common/PlayManager.ts create mode 100644 frontend/pc-tool/src/packages/pc-editor/common/TrackManager.ts diff --git a/frontend/pc-tool/public/iconfont/demo_index.html b/frontend/pc-tool/public/iconfont/demo_index.html index ee879a95..7bd1271c 100644 --- a/frontend/pc-tool/public/iconfont/demo_index.html +++ b/frontend/pc-tool/public/iconfont/demo_index.html @@ -1,418 +1,738 @@ - - - iconfont Demo - - - - - - - - - - - - - -
-

-

- -
-
-
    -
  • - -
    file
    -
    &#xe73c;
    -
  • - -
  • - -
    review
    -
    &#xe739;
    -
  • - -
  • - -
    download
    -
    &#xe73a;
    -
  • - -
  • - -
    open
    -
    &#xe73b;
    -
  • - -
  • - -
    polyline
    -
    &#xe61b;
    -
  • - -
  • - -
    ai
    -
    &#xe617;
    -
  • - -
  • - -
    rect
    -
    &#xe618;
    -
  • - -
  • - -
    edit
    -
    &#xe619;
    -
  • - -
  • - -
    polygon
    -
    &#xe61a;
    -
  • - -
  • - -
    link
    -
    &#xe735;
    -
  • - -
  • - -
    cube
    -
    &#xe736;
    -
  • - -
  • - -
    loading
    -
    &#xe737;
    -
  • - -
  • - -
    model
    -
    &#xe738;
    -
  • - -
  • - -
    folding
    -
    &#xe732;
    -
  • - -
  • - -
    right
    -
    &#xe731;
    -
  • - -
  • - -
    left
    -
    &#xe730;
    -
  • - -
  • - -
    hidden
    -
    &#xe72f;
    -
  • - -
  • - -
    an-an
    -
    &#xe72e;
    -
  • - -
  • - -
    more
    -
    &#xe72d;
    -
  • - -
  • - -
    Auxiliary line
    -
    &#xe72c;
    -
  • - -
  • - -
    target
    -
    &#xe72b;
    -
  • - -
  • - -
    cube
    -
    &#xe726;
    -
  • - -
  • - -
    The standard frame
    -
    &#xe727;
    -
  • - -
  • - -
    view
    -
    &#xe71f;
    -
  • - -
  • - -
    delete
    -
    &#xe721;
    -
  • - -
  • - -
    Edit the label
    -
    &#xe722;
    -
  • - -
  • - -
    screening
    -
    &#xe724;
    -
  • - -
  • - -
    notation-tool
    -
    &#xe71b;
    -
  • - -
  • - -
    Add a notation
    -
    &#xe71c;
    -
  • - -
  • - -
    Have been added
    -
    &#xe71d;
    -
  • - -
  • - -
    Notes - list
    -
    &#xe71e;
    -
  • - -
  • - -
    remind
    -
    &#xe71a;
    -
  • - -
  • - -
    Left rotation
    -
    &#xe714;
    -
  • - -
  • - -
    Right rotation
    -
    &#xe715;
    -
  • - -
  • - -
    up
    -
    &#xe718;
    -
  • - -
  • - -
    down
    -
    &#xe719;
    -
  • - -
  • - -
    an-right
    -
    &#xe713;
    -
  • - -
  • - -
    Pack up
    -
    &#xe712;
    -
  • - -
  • - -
    tool
    -
    &#xe70e;
    -
  • - -
  • - -
    move
    -
    &#xe70f;
    -
  • - -
  • - -
    rotating
    -
    &#xe710;
    -
  • - -
  • - -
    mapping
    -
    &#xe711;
    -
  • - -
  • - -
    information
    -
    &#xe70b;
    -
  • - -
  • - -
    According to
    -
    &#xe70c;
    -
  • - -
  • - -
    submit
    -
    &#xe709;
    -
  • - -
  • - -
    hang up
    -
    &#xe708;
    -
  • - -
  • - -
    Exit full screen
    -
    &#xe706;
    -
  • - -
  • - -
    Full screen
    -
    &#xe705;
    -
  • - -
  • - -
    help
    -
    &#xe704;
    -
  • - -
  • - -
    save
    -
    &#xe703;
    -
  • - -
  • - -
    Work flow
    -
    &#xe702;
    -
  • - -
  • - -
    Expiration time
    -
    &#xe701;
    -
  • - -
  • - -
    Job information
    -
    &#xe6fe;
    -
  • -
-
-

Unicode 引用

-
- -

Unicode 是字体在网页端最原始的应用方式,特点是:

-
    -
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • -
  • 默认情况下不支持多色,直接添加多色图标会自动去色。
  • -
-
-

注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol - 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)

-
-

Unicode 使用步骤如下:

-

第一步:拷贝项目下面生成的 @font-face

-

+  
+  iconfont Demo
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+
+
+  
+

+ + +

+ +
+
+
    + +
  • + +
    biaoqian
    +
    &#xe76f;
    +
  • + +
  • + +
    polyline-v1
    +
    &#xe602;
    +
  • + +
  • + +
    polygon-v1
    +
    &#xe601;
    +
  • + +
  • + +
    ruler
    +
    &#xe6a7;
    +
  • + +
  • + +
    lunkuo
    +
    &#xe76d;
    +
  • + +
  • + +
    jiance
    +
    &#xe76e;
    +
  • + +
  • + +
    Hotkeys
    +
    &#xe767;
    +
  • + +
  • + +
    rect
    +
    &#xe67c;
    +
  • + +
  • + +
    bone
    +
    &#xe68e;
    +
  • + +
  • + +
    splinecurve
    +
    &#xe698;
    +
  • + +
  • + +
    polygon
    +
    &#xe69b;
    +
  • + +
  • + +
    unclosedpolygon
    +
    &#xe6a3;
    +
  • + +
  • + +
    beisaierquxian
    +
    &#xe7ab;
    +
  • + +
  • + +
    point
    +
    &#xe76c;
    +
  • + +
  • + +
    liebiao
    +
    &#xe768;
    +
  • + +
  • + +
    Crop non-first object
    +
    &#xe769;
    +
  • + +
  • + +
    Crop the first object
    +
    &#xe76a;
    +
  • + +
  • + +
    图层显示
    +
    &#xe76b;
    +
  • + +
  • + +
    group
    +
    &#xe762;
    +
  • + +
  • + +
    tuichuzu
    +
    &#xe763;
    +
  • + +
  • + +
    ai
    +
    &#xe764;
    +
  • + +
  • + +
    cancel
    +
    &#xe765;
    +
  • + +
  • + +
    jiaohushi
    +
    &#xe766;
    +
  • + +
  • + +
    add
    +
    &#xe758;
    +
  • + +
  • + +
    more
    +
    &#xe759;
    +
  • + +
  • + +
    error
    +
    &#xe75a;
    +
  • + +
  • + +
    fangda
    +
    &#xe75b;
    +
  • + +
  • + +
    cube
    +
    &#xe75c;
    +
  • + +
  • + +
    No result in this frame
    +
    &#xe75d;
    +
  • + +
  • + +
    ai kuang
    +
    &#xe75e;
    +
  • + +
  • + +
    noun-isometric
    +
    &#xe75f;
    +
  • + +
  • + +
    Ground Truth in all frames
    +
    &#xe760;
    +
  • + +
  • + +
    Ground truth
    +
    &#xe761;
    +
  • + +
  • + +
    restore
    +
    &#xe757;
    +
  • + +
  • + +
    Acceptance
    +
    &#xe752;
    +
  • + +
  • + +
    review
    +
    &#xe753;
    +
  • + +
  • + +
    Annotate
    +
    &#xe754;
    +
  • + +
  • + +
    Modify
    +
    &#xe755;
    +
  • + +
  • + +
    QA
    +
    &#xe756;
    +
  • + +
  • + +
    arrow
    +
    &#xe751;
    +
  • + +
  • + +
    suspend
    +
    &#xe74f;
    +
  • + +
  • + +
    ongoing
    +
    &#xe750;
    +
  • + +
  • + +
    Classfication
    +
    &#xe74b;
    +
  • + +
  • + +
    Comments
    +
    &#xe74c;
    +
  • + +
  • + +
    Results
    +
    &#xe74d;
    +
  • + +
  • + +
    Validity
    +
    &#xe74e;
    +
  • + +
  • + +
    Group
    +
    &#xe74a;
    +
  • + +
  • + +
    abnormal
    +
    &#xe748;
    +
  • + +
  • + +
    rectangular
    +
    &#xe747;
    +
  • + +
  • + +
    3d
    +
    &#xe749;
    +
  • + +
  • + +
    bofang
    +
    &#xe741;
    +
  • + +
  • + +
    hebing
    +
    &#xe742;
    +
  • + +
  • + +
    chongxinbofang
    +
    &#xe743;
    +
  • + +
  • + +
    jiancha
    +
    &#xe744;
    +
  • + +
  • + +
    zuo-fuzhi
    +
    &#xe745;
    +
  • + +
  • + +
    right-fuzhi
    +
    &#xe746;
    +
  • + +
  • + +
    file
    +
    &#xe73c;
    +
  • + +
  • + +
    review
    +
    &#xe739;
    +
  • + +
  • + +
    download
    +
    &#xe73a;
    +
  • + +
  • + +
    open
    +
    &#xe73b;
    +
  • + +
  • + +
    polyline
    +
    &#xe61b;
    +
  • + +
  • + +
    ai
    +
    &#xe617;
    +
  • + +
  • + +
    rect
    +
    &#xe618;
    +
  • + +
  • + +
    edit
    +
    &#xe619;
    +
  • + +
  • + +
    polygon
    +
    &#xe61a;
    +
  • + +
  • + +
    link
    +
    &#xe735;
    +
  • + +
  • + +
    cube
    +
    &#xe736;
    +
  • + +
  • + +
    loading
    +
    &#xe737;
    +
  • + +
  • + +
    model
    +
    &#xe738;
    +
  • + +
  • + +
    folding
    +
    &#xe732;
    +
  • + +
  • + +
    right
    +
    &#xe731;
    +
  • + +
  • + +
    left
    +
    &#xe730;
    +
  • + +
  • + +
    hidden
    +
    &#xe72f;
    +
  • + +
  • + +
    an-an
    +
    &#xe72e;
    +
  • + +
  • + +
    more
    +
    &#xe72d;
    +
  • + +
  • + +
    Auxiliary line
    +
    &#xe72c;
    +
  • + +
  • + +
    target
    +
    &#xe72b;
    +
  • + +
  • + +
    cube
    +
    &#xe726;
    +
  • + +
  • + +
    The standard frame
    +
    &#xe727;
    +
  • + +
  • + +
    view
    +
    &#xe71f;
    +
  • + +
  • + +
    delete
    +
    &#xe721;
    +
  • + +
  • + +
    Edit the label
    +
    &#xe722;
    +
  • + +
  • + +
    screening
    +
    &#xe724;
    +
  • + +
  • + +
    notation-tool
    +
    &#xe71b;
    +
  • + +
  • + +
    Add a notation
    +
    &#xe71c;
    +
  • + +
  • + +
    Have been added
    +
    &#xe71d;
    +
  • + +
  • + +
    Notes - list
    +
    &#xe71e;
    +
  • + +
  • + +
    remind
    +
    &#xe71a;
    +
  • + +
  • + +
    Left rotation
    +
    &#xe714;
    +
  • + +
  • + +
    Right rotation
    +
    &#xe715;
    +
  • + +
  • + +
    up
    +
    &#xe718;
    +
  • + +
  • + +
    down
    +
    &#xe719;
    +
  • + +
  • + +
    an-right
    +
    &#xe713;
    +
  • + +
  • + +
    Pack up
    +
    &#xe712;
    +
  • + +
  • + +
    tool
    +
    &#xe70e;
    +
  • + +
  • + +
    move
    +
    &#xe70f;
    +
  • + +
  • + +
    rotating
    +
    &#xe710;
    +
  • + +
  • + +
    mapping
    +
    &#xe711;
    +
  • + +
  • + +
    information
    +
    &#xe70b;
    +
  • + +
  • + +
    According to
    +
    &#xe70c;
    +
  • + +
  • + +
    submit
    +
    &#xe709;
    +
  • + +
  • + +
    hang up
    +
    &#xe708;
    +
  • + +
  • + +
    Exit full screen
    +
    &#xe706;
    +
  • + +
  • + +
    Full screen
    +
    &#xe705;
    +
  • + +
  • + +
    help
    +
    &#xe704;
    +
  • + +
  • + +
    save
    +
    &#xe703;
    +
  • + +
  • + +
    Work flow
    +
    &#xe702;
    +
  • + +
  • + +
    Expiration time
    +
    &#xe701;
    +
  • + +
  • + +
    Job information
    +
    &#xe6fe;
    +
  • + +
+
+

Unicode 引用

+
+ +

Unicode 是字体在网页端最原始的应用方式,特点是:

+
    +
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • +
  • 默认情况下不支持多色,直接添加多色图标会自动去色。
  • +
+
+

注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)

+
+

Unicode 使用步骤如下:

+

第一步:拷贝项目下面生成的 @font-face

+
@font-face {
   font-family: 'iconfont';
-  src: url('iconfont.woff2?t=1659510737631') format('woff2'),
-       url('iconfont.woff?t=1659510737631') format('woff'),
-       url('iconfont.ttf?t=1659510737631') format('truetype');
+  src: url('iconfont.woff2?t=1693477727245') format('woff2'),
+       url('iconfont.woff?t=1693477727245') format('woff'),
+       url('iconfont.ttf?t=1693477727245') format('truetype');
 }
 
-

第二步:定义使用 iconfont 的样式

-
第二步:定义使用 iconfont 的样式
+
.iconfont {
   font-family: "iconfont" !important;
   font-size: 16px;
@@ -421,825 +741,1918 @@ 

第二步:定义使用 iconfont 的样式

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

第三步:挑选相应图标并获取字体编码,应用于页面

-
+          

第三步:挑选相应图标并获取字体编码,应用于页面

+
 <span class="iconfont">&#x33;</span>
 
-
-

"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 - "iconfont"。

-
-
-
-
-
    -
  • - -
    file
    -
    .icon-file
    -
  • - -
  • - -
    review
    -
    .icon-review
    -
  • - -
  • - -
    download
    -
    .icon-download
    -
  • - -
  • - -
    open
    -
    .icon-open
    -
  • - -
  • - -
    polyline
    -
    .icon-polyline
    -
  • - -
  • - -
    ai
    -
    .icon-ai
    -
  • - -
  • - -
    rect
    -
    .icon-rect
    -
  • - -
  • - -
    edit
    -
    .icon-edit
    -
  • - -
  • - -
    polygon
    -
    .icon-polygon
    -
  • - -
  • - -
    link
    -
    .icon-Frame
    -
  • - -
  • - -
    cube
    -
    .icon-a-122
    -
  • - -
  • - -
    loading
    -
    .icon-loading
    -
  • - -
  • - -
    model
    -
    .icon-Vector
    -
  • - -
  • - -
    folding
    -
    .icon-dakai
    -
  • - -
  • - -
    right
    -
    .icon-right
    -
  • - -
  • - -
    left
    -
    .icon-a-zu25265
    -
  • - -
  • - -
    hidden
    -
    .icon-a-zu25263
    -
  • - -
  • - -
    an-an
    -
    .icon-a-zu25262
    -
  • - -
  • - -
    more
    -
    .icon-gengduoicon
    -
  • - -
  • - -
    Auxiliary line
    -
    .icon-fuzhuxian
    -
  • - -
  • - -
    target
    -
    .icon-mubiao
    -
  • - -
  • - -
    cube
    -
    .icon-lifangti
    -
  • - -
  • - -
    The standard frame
    -
    .icon-biaozhunkuang
    -
  • - -
  • - -
    view
    -
    .icon-yanjing
    -
  • - -
  • - -
    delete
    -
    .icon-shanchuicon
    -
  • - -
  • - -
    Edit the label
    -
    .icon-bianjibiaoqian
    -
  • - -
  • - -
    screening
    -
    .icon-shaixuan
    -
  • - -
  • - -
    notation-tool
    -
    .icon-pizhuicon
    -
  • - -
  • - -
    Add a notation
    -
    .icon-tianjiapizhu
    -
  • - -
  • - -
    Have been added
    -
    .icon-yitianjia
    -
  • - -
  • - -
    Notes - list
    -
    .icon-pizhu
    -
  • - -
  • - -
    remind
    -
    .icon-tixing
    -
  • - -
  • - -
    Left rotation
    -
    .icon-zuoxuanze
    -
  • - -
  • - -
    Right rotation
    -
    .icon-youxuanzhuan
    -
  • - -
  • - -
    up
    -
    .icon-shang
    -
  • - -
  • - -
    down
    -
    .icon-xia
    -
  • - -
  • - -
    an-right
    -
    .icon-zhankai1
    -
  • - -
  • - -
    Pack up
    -
    .icon-shouqi
    -
  • - -
  • - -
    tool
    -
    .icon-gongju
    -
  • - -
  • - -
    move
    -
    .icon-yidong
    -
  • - -
  • - -
    rotating
    -
    .icon-xuanzhuan
    -
  • - -
  • - -
    mapping
    -
    .icon-yingshe
    -
  • - -
  • - -
    information
    -
    .icon-xinxi
    -
  • - -
  • - -
    According to
    -
    .icon-xianshi
    -
  • - -
  • - -
    submit
    -
    .icon-tijiao
    -
  • - -
  • - -
    hang up
    -
    .icon-guaqi
    -
  • - -
  • - -
    Exit full screen
    -
    .icon-tuichuquanping
    -
  • - -
  • - -
    Full screen
    -
    .icon-a-Fullscreen
    -
  • - -
  • - -
    help
    -
    .icon-help
    -
  • - -
  • - -
    save
    -
    .icon-save
    -
  • - -
  • - -
    Work flow
    -
    .icon-a-Workflow
    -
  • - -
  • - -
    Expiration time
    -
    .icon-a-lujing15750
    -
  • - -
  • - -
    Job information
    -
    .icon-a-Jobinformation
    -
  • -
-
-

font-class 引用

-
- -

font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode - 书写不直观,语意不明确的问题。

-

与 Unicode 使用方式相比,具有如下特点:

-
    -
  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon - 是什么。
  • -
  • 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class - 里面的 Unicode 引用。
  • -
-

使用步骤如下:

-

第一步:引入项目下面生成的 fontclass 代码:

-
<link rel="stylesheet" href="./iconfont.css">
+          
+

"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    + biaoqian +
    +
    .icon-label +
    +
  • + +
  • + +
    + polyline-v1 +
    +
    .icon-polyline-v1 +
    +
  • + +
  • + +
    + polygon-v1 +
    +
    .icon-polygon-v1 +
    +
  • + +
  • + +
    + ruler +
    +
    .icon-ruler +
    +
  • + +
  • + +
    + lunkuo +
    +
    .icon-lunkuo +
    +
  • + +
  • + +
    + jiance +
    +
    .icon-jiance +
    +
  • + +
  • + +
    + Hotkeys +
    +
    .icon-Hotkeys +
    +
  • + +
  • + +
    + rect +
    +
    .icon-rect1 +
    +
  • + +
  • + +
    + bone +
    +
    .icon-bone +
    +
  • + +
  • + +
    + splinecurve +
    +
    .icon-splinecurve +
    +
  • + +
  • + +
    + polygon +
    +
    .icon-polygon1 +
    +
  • + +
  • + +
    + unclosedpolygon +
    +
    .icon-unclosedpolygon +
    +
  • + +
  • + +
    + beisaierquxian +
    +
    .icon-bezier-curve +
    +
  • + +
  • + +
    + point +
    +
    .icon-point +
    +
  • + +
  • + +
    + liebiao +
    +
    .icon-liebiao +
    +
  • + +
  • + +
    + Crop non-first object +
    +
    .icon-a-Cropnon-firstobject +
    +
  • + +
  • + +
    + Crop the first object +
    +
    .icon-a-Cropthefirstobject +
    +
  • + +
  • + +
    + 图层显示 +
    +
    .icon-tucengxianshi +
    +
  • + +
  • + +
    + group +
    +
    .icon-group +
    +
  • + +
  • + +
    + tuichuzu +
    +
    .icon-tuichuzu +
    +
  • + +
  • + +
    + ai +
    +
    .icon-ai1 +
    +
  • + +
  • + +
    + cancel +
    +
    .icon-cancel +
    +
  • + +
  • + +
    + jiaohushi +
    +
    .icon-jiaohushi +
    +
  • + +
  • + +
    + add +
    +
    .icon-add +
    +
  • + +
  • + +
    + more +
    +
    .icon-more +
    +
  • + +
  • + +
    + error +
    +
    .icon-error +
    +
  • + +
  • + +
    + fangda +
    +
    .icon-fangda +
    +
  • + +
  • + +
    + cube +
    +
    .icon-cube +
    +
  • + +
  • + +
    + No result in this frame +
    +
    .icon-a-Noresultinthisframe +
    +
  • + +
  • + +
    + ai kuang +
    +
    .icon-a-aikuang +
    +
  • + +
  • + +
    + noun-isometric +
    +
    .icon-noun-isometric +
    +
  • + +
  • + +
    + Ground Truth in all frames +
    +
    .icon-a-GroundTruthinallframes +
    +
  • + +
  • + +
    + Ground truth +
    +
    .icon-a-Groundtruth +
    +
  • + +
  • + +
    + restore +
    +
    .icon-huifu +
    +
  • + +
  • + +
    + Acceptance +
    +
    .icon-Acceptance +
    +
  • + +
  • + +
    + review +
    +
    .icon-review1 +
    +
  • + +
  • + +
    + Annotate +
    +
    .icon-Annotate +
    +
  • + +
  • + +
    + Modify +
    +
    .icon-Modify +
    +
  • + +
  • + +
    + QA +
    +
    .icon-QA +
    +
  • + +
  • + +
    + arrow +
    +
    .icon-Frame3 +
    +
  • + +
  • + +
    + suspend +
    +
    .icon-Frame2 +
    +
  • + +
  • + +
    + ongoing +
    +
    .icon-Frame23 +
    +
  • + +
  • + +
    + Classfication +
    +
    .icon-Classfication +
    +
  • + +
  • + +
    + Comments +
    +
    .icon-Comments +
    +
  • + +
  • + +
    + Results +
    +
    .icon-Results +
    +
  • + +
  • + +
    + Validity +
    +
    .icon-Validity +
    +
  • + +
  • + +
    + Group +
    +
    .icon-Group +
    +
  • + +
  • + +
    + abnormal +
    +
    .icon-Frame1 +
    +
  • + +
  • + +
    + rectangular +
    +
    .icon-rectangular +
    +
  • + +
  • + +
    + 3d +
    +
    .icon-a-3d +
    +
  • + +
  • + +
    + bofang +
    +
    .icon-bofang +
    +
  • + +
  • + +
    + hebing +
    +
    .icon-hebing +
    +
  • + +
  • + +
    + chongxinbofang +
    +
    .icon-chongxinbofang +
    +
  • + +
  • + +
    + jiancha +
    +
    .icon-jiancha +
    +
  • + +
  • + +
    + zuo-fuzhi +
    +
    .icon-zuo-fuzhi +
    +
  • + +
  • + +
    + right-fuzhi +
    +
    .icon-right-fuzhi +
    +
  • + +
  • + +
    + file +
    +
    .icon-file +
    +
  • + +
  • + +
    + review +
    +
    .icon-review +
    +
  • + +
  • + +
    + download +
    +
    .icon-download +
    +
  • + +
  • + +
    + open +
    +
    .icon-open +
    +
  • + +
  • + +
    + polyline +
    +
    .icon-polyline +
    +
  • + +
  • + +
    + ai +
    +
    .icon-ai +
    +
  • + +
  • + +
    + rect +
    +
    .icon-rect +
    +
  • + +
  • + +
    + edit +
    +
    .icon-edit +
    +
  • + +
  • + +
    + polygon +
    +
    .icon-polygon +
    +
  • + +
  • + +
    + link +
    +
    .icon-Frame +
    +
  • + +
  • + +
    + cube +
    +
    .icon-a-122 +
    +
  • + +
  • + +
    + loading +
    +
    .icon-loading +
    +
  • + +
  • + +
    + model +
    +
    .icon-Vector +
    +
  • + +
  • + +
    + folding +
    +
    .icon-dakai +
    +
  • + +
  • + +
    + right +
    +
    .icon-right +
    +
  • + +
  • + +
    + left +
    +
    .icon-a-zu25265 +
    +
  • + +
  • + +
    + hidden +
    +
    .icon-a-zu25263 +
    +
  • + +
  • + +
    + an-an +
    +
    .icon-a-zu25262 +
    +
  • + +
  • + +
    + more +
    +
    .icon-gengduoicon +
    +
  • + +
  • + +
    + Auxiliary line +
    +
    .icon-fuzhuxian +
    +
  • + +
  • + +
    + target +
    +
    .icon-mubiao +
    +
  • + +
  • + +
    + cube +
    +
    .icon-lifangti +
    +
  • + +
  • + +
    + The standard frame +
    +
    .icon-biaozhunkuang +
    +
  • + +
  • + +
    + view +
    +
    .icon-yanjing +
    +
  • + +
  • + +
    + delete +
    +
    .icon-shanchuicon +
    +
  • + +
  • + +
    + Edit the label +
    +
    .icon-bianjibiaoqian +
    +
  • + +
  • + +
    + screening +
    +
    .icon-shaixuan +
    +
  • + +
  • + +
    + notation-tool +
    +
    .icon-pizhuicon +
    +
  • + +
  • + +
    + Add a notation +
    +
    .icon-tianjiapizhu +
    +
  • + +
  • + +
    + Have been added +
    +
    .icon-yitianjia +
    +
  • + +
  • + +
    + Notes - list +
    +
    .icon-pizhu +
    +
  • + +
  • + +
    + remind +
    +
    .icon-tixing +
    +
  • + +
  • + +
    + Left rotation +
    +
    .icon-zuoxuanze +
    +
  • + +
  • + +
    + Right rotation +
    +
    .icon-youxuanzhuan +
    +
  • + +
  • + +
    + up +
    +
    .icon-shang +
    +
  • + +
  • + +
    + down +
    +
    .icon-xia +
    +
  • + +
  • + +
    + an-right +
    +
    .icon-zhankai1 +
    +
  • + +
  • + +
    + Pack up +
    +
    .icon-shouqi +
    +
  • + +
  • + +
    + tool +
    +
    .icon-gongju +
    +
  • + +
  • + +
    + move +
    +
    .icon-yidong +
    +
  • + +
  • + +
    + rotating +
    +
    .icon-xuanzhuan +
    +
  • + +
  • + +
    + mapping +
    +
    .icon-yingshe +
    +
  • + +
  • + +
    + information +
    +
    .icon-xinxi +
    +
  • + +
  • + +
    + According to +
    +
    .icon-xianshi +
    +
  • + +
  • + +
    + submit +
    +
    .icon-tijiao +
    +
  • + +
  • + +
    + hang up +
    +
    .icon-guaqi +
    +
  • + +
  • + +
    + Exit full screen +
    +
    .icon-tuichuquanping +
    +
  • + +
  • + +
    + Full screen +
    +
    .icon-a-Fullscreen +
    +
  • + +
  • + +
    + help +
    +
    .icon-help +
    +
  • + +
  • + +
    + save +
    +
    .icon-save +
    +
  • + +
  • + +
    + Work flow +
    +
    .icon-a-Workflow +
    +
  • + +
  • + +
    + Expiration time +
    +
    .icon-a-lujing15750 +
    +
  • + +
  • + +
    + Job information +
    +
    .icon-a-Jobinformation +
    +
  • + +
+
+

font-class 引用

+
+ +

font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。

+

与 Unicode 使用方式相比,具有如下特点:

+
    +
  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
  • +
  • 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 fontclass 代码:

+
<link rel="stylesheet" href="./iconfont.css">
 
-

第二步:挑选相应图标并获取类名,应用于页面:

-
<span class="iconfont icon-xxx"></span>
+        

第二步:挑选相应图标并获取类名,应用于页面:

+
<span class="iconfont icon-xxx"></span>
 
-
-

" iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 - "iconfont"。

-
-
-
-
-
    -
  • - -
    file
    -
    #icon-file
    -
  • - -
  • - -
    review
    -
    #icon-review
    -
  • - -
  • - -
    download
    -
    #icon-download
    -
  • - -
  • - -
    open
    -
    #icon-open
    -
  • - -
  • - -
    polyline
    -
    #icon-polyline
    -
  • - -
  • - -
    ai
    -
    #icon-ai
    -
  • - -
  • - -
    rect
    -
    #icon-rect
    -
  • - -
  • - -
    edit
    -
    #icon-edit
    -
  • - -
  • - -
    polygon
    -
    #icon-polygon
    -
  • - -
  • - -
    link
    -
    #icon-Frame
    -
  • - -
  • - -
    cube
    -
    #icon-a-122
    -
  • - -
  • - -
    loading
    -
    #icon-loading
    -
  • - -
  • - -
    model
    -
    #icon-Vector
    -
  • - -
  • - -
    folding
    -
    #icon-dakai
    -
  • - -
  • - -
    right
    -
    #icon-right
    -
  • - -
  • - -
    left
    -
    #icon-a-zu25265
    -
  • - -
  • - -
    hidden
    -
    #icon-a-zu25263
    -
  • - -
  • - -
    an-an
    -
    #icon-a-zu25262
    -
  • - -
  • - -
    more
    -
    #icon-gengduoicon
    -
  • - -
  • - -
    Auxiliary line
    -
    #icon-fuzhuxian
    -
  • - -
  • - -
    target
    -
    #icon-mubiao
    -
  • - -
  • - -
    cube
    -
    #icon-lifangti
    -
  • - -
  • - -
    The standard frame
    -
    #icon-biaozhunkuang
    -
  • - -
  • - -
    view
    -
    #icon-yanjing
    -
  • - -
  • - -
    delete
    -
    #icon-shanchuicon
    -
  • - -
  • - -
    Edit the label
    -
    #icon-bianjibiaoqian
    -
  • - -
  • - -
    screening
    -
    #icon-shaixuan
    -
  • - -
  • - -
    notation-tool
    -
    #icon-pizhuicon
    -
  • - -
  • - -
    Add a notation
    -
    #icon-tianjiapizhu
    -
  • - -
  • - -
    Have been added
    -
    #icon-yitianjia
    -
  • - -
  • - -
    Notes - list
    -
    #icon-pizhu
    -
  • - -
  • - -
    remind
    -
    #icon-tixing
    -
  • - -
  • - -
    Left rotation
    -
    #icon-zuoxuanze
    -
  • - -
  • - -
    Right rotation
    -
    #icon-youxuanzhuan
    -
  • - -
  • - -
    up
    -
    #icon-shang
    -
  • - -
  • - -
    down
    -
    #icon-xia
    -
  • - -
  • - -
    an-right
    -
    #icon-zhankai1
    -
  • - -
  • - -
    Pack up
    -
    #icon-shouqi
    -
  • - -
  • - -
    tool
    -
    #icon-gongju
    -
  • - -
  • - -
    move
    -
    #icon-yidong
    -
  • - -
  • - -
    rotating
    -
    #icon-xuanzhuan
    -
  • - -
  • - -
    mapping
    -
    #icon-yingshe
    -
  • - -
  • - -
    information
    -
    #icon-xinxi
    -
  • - -
  • - -
    According to
    -
    #icon-xianshi
    -
  • - -
  • - -
    submit
    -
    #icon-tijiao
    -
  • - -
  • - -
    hang up
    -
    #icon-guaqi
    -
  • - -
  • - -
    Exit full screen
    -
    #icon-tuichuquanping
    -
  • - -
  • - -
    Full screen
    -
    #icon-a-Fullscreen
    -
  • - -
  • - -
    help
    -
    #icon-help
    -
  • - -
  • - -
    save
    -
    #icon-save
    -
  • - -
  • - -
    Work flow
    -
    #icon-a-Workflow
    -
  • - -
  • - -
    Expiration time
    -
    #icon-a-lujing15750
    -
  • - -
  • - -
    Job information
    -
    #icon-a-Jobinformation
    -
  • -
-
-

Symbol 引用

-
- -

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 - 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:

-
    -
  • 支持多色图标了,不再受单色限制。
  • -
  • 通过一些技巧,支持像字体那样,通过 font-size, - color 来调整样式。
  • -
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • -
  • 浏览器渲染 SVG 的性能一般,还不如 png。
  • -
-

使用步骤如下:

-

第一步:引入项目下面生成的 symbol 代码:

-
<script src="./iconfont.js"></script>
+        
+

" + iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    biaoqian
    +
    #icon-label
    +
  • + +
  • + +
    polyline-v1
    +
    #icon-polyline-v1
    +
  • + +
  • + +
    polygon-v1
    +
    #icon-polygon-v1
    +
  • + +
  • + +
    ruler
    +
    #icon-ruler
    +
  • + +
  • + +
    lunkuo
    +
    #icon-lunkuo
    +
  • + +
  • + +
    jiance
    +
    #icon-jiance
    +
  • + +
  • + +
    Hotkeys
    +
    #icon-Hotkeys
    +
  • + +
  • + +
    rect
    +
    #icon-rect1
    +
  • + +
  • + +
    bone
    +
    #icon-bone
    +
  • + +
  • + +
    splinecurve
    +
    #icon-splinecurve
    +
  • + +
  • + +
    polygon
    +
    #icon-polygon1
    +
  • + +
  • + +
    unclosedpolygon
    +
    #icon-unclosedpolygon
    +
  • + +
  • + +
    beisaierquxian
    +
    #icon-bezier-curve
    +
  • + +
  • + +
    point
    +
    #icon-point
    +
  • + +
  • + +
    liebiao
    +
    #icon-liebiao
    +
  • + +
  • + +
    Crop non-first object
    +
    #icon-a-Cropnon-firstobject
    +
  • + +
  • + +
    Crop the first object
    +
    #icon-a-Cropthefirstobject
    +
  • + +
  • + +
    图层显示
    +
    #icon-tucengxianshi
    +
  • + +
  • + +
    group
    +
    #icon-group
    +
  • + +
  • + +
    tuichuzu
    +
    #icon-tuichuzu
    +
  • + +
  • + +
    ai
    +
    #icon-ai1
    +
  • + +
  • + +
    cancel
    +
    #icon-cancel
    +
  • + +
  • + +
    jiaohushi
    +
    #icon-jiaohushi
    +
  • + +
  • + +
    add
    +
    #icon-add
    +
  • + +
  • + +
    more
    +
    #icon-more
    +
  • + +
  • + +
    error
    +
    #icon-error
    +
  • + +
  • + +
    fangda
    +
    #icon-fangda
    +
  • + +
  • + +
    cube
    +
    #icon-cube
    +
  • + +
  • + +
    No result in this frame
    +
    #icon-a-Noresultinthisframe
    +
  • + +
  • + +
    ai kuang
    +
    #icon-a-aikuang
    +
  • + +
  • + +
    noun-isometric
    +
    #icon-noun-isometric
    +
  • + +
  • + +
    Ground Truth in all frames
    +
    #icon-a-GroundTruthinallframes
    +
  • + +
  • + +
    Ground truth
    +
    #icon-a-Groundtruth
    +
  • + +
  • + +
    restore
    +
    #icon-huifu
    +
  • + +
  • + +
    Acceptance
    +
    #icon-Acceptance
    +
  • + +
  • + +
    review
    +
    #icon-review1
    +
  • + +
  • + +
    Annotate
    +
    #icon-Annotate
    +
  • + +
  • + +
    Modify
    +
    #icon-Modify
    +
  • + +
  • + +
    QA
    +
    #icon-QA
    +
  • + +
  • + +
    arrow
    +
    #icon-Frame3
    +
  • + +
  • + +
    suspend
    +
    #icon-Frame2
    +
  • + +
  • + +
    ongoing
    +
    #icon-Frame23
    +
  • + +
  • + +
    Classfication
    +
    #icon-Classfication
    +
  • + +
  • + +
    Comments
    +
    #icon-Comments
    +
  • + +
  • + +
    Results
    +
    #icon-Results
    +
  • + +
  • + +
    Validity
    +
    #icon-Validity
    +
  • + +
  • + +
    Group
    +
    #icon-Group
    +
  • + +
  • + +
    abnormal
    +
    #icon-Frame1
    +
  • + +
  • + +
    rectangular
    +
    #icon-rectangular
    +
  • + +
  • + +
    3d
    +
    #icon-a-3d
    +
  • + +
  • + +
    bofang
    +
    #icon-bofang
    +
  • + +
  • + +
    hebing
    +
    #icon-hebing
    +
  • + +
  • + +
    chongxinbofang
    +
    #icon-chongxinbofang
    +
  • + +
  • + +
    jiancha
    +
    #icon-jiancha
    +
  • + +
  • + +
    zuo-fuzhi
    +
    #icon-zuo-fuzhi
    +
  • + +
  • + +
    right-fuzhi
    +
    #icon-right-fuzhi
    +
  • + +
  • + +
    file
    +
    #icon-file
    +
  • + +
  • + +
    review
    +
    #icon-review
    +
  • + +
  • + +
    download
    +
    #icon-download
    +
  • + +
  • + +
    open
    +
    #icon-open
    +
  • + +
  • + +
    polyline
    +
    #icon-polyline
    +
  • + +
  • + +
    ai
    +
    #icon-ai
    +
  • + +
  • + +
    rect
    +
    #icon-rect
    +
  • + +
  • + +
    edit
    +
    #icon-edit
    +
  • + +
  • + +
    polygon
    +
    #icon-polygon
    +
  • + +
  • + +
    link
    +
    #icon-Frame
    +
  • + +
  • + +
    cube
    +
    #icon-a-122
    +
  • + +
  • + +
    loading
    +
    #icon-loading
    +
  • + +
  • + +
    model
    +
    #icon-Vector
    +
  • + +
  • + +
    folding
    +
    #icon-dakai
    +
  • + +
  • + +
    right
    +
    #icon-right
    +
  • + +
  • + +
    left
    +
    #icon-a-zu25265
    +
  • + +
  • + +
    hidden
    +
    #icon-a-zu25263
    +
  • + +
  • + +
    an-an
    +
    #icon-a-zu25262
    +
  • + +
  • + +
    more
    +
    #icon-gengduoicon
    +
  • + +
  • + +
    Auxiliary line
    +
    #icon-fuzhuxian
    +
  • + +
  • + +
    target
    +
    #icon-mubiao
    +
  • + +
  • + +
    cube
    +
    #icon-lifangti
    +
  • + +
  • + +
    The standard frame
    +
    #icon-biaozhunkuang
    +
  • + +
  • + +
    view
    +
    #icon-yanjing
    +
  • + +
  • + +
    delete
    +
    #icon-shanchuicon
    +
  • + +
  • + +
    Edit the label
    +
    #icon-bianjibiaoqian
    +
  • + +
  • + +
    screening
    +
    #icon-shaixuan
    +
  • + +
  • + +
    notation-tool
    +
    #icon-pizhuicon
    +
  • + +
  • + +
    Add a notation
    +
    #icon-tianjiapizhu
    +
  • + +
  • + +
    Have been added
    +
    #icon-yitianjia
    +
  • + +
  • + +
    Notes - list
    +
    #icon-pizhu
    +
  • + +
  • + +
    remind
    +
    #icon-tixing
    +
  • + +
  • + +
    Left rotation
    +
    #icon-zuoxuanze
    +
  • + +
  • + +
    Right rotation
    +
    #icon-youxuanzhuan
    +
  • + +
  • + +
    up
    +
    #icon-shang
    +
  • + +
  • + +
    down
    +
    #icon-xia
    +
  • + +
  • + +
    an-right
    +
    #icon-zhankai1
    +
  • + +
  • + +
    Pack up
    +
    #icon-shouqi
    +
  • + +
  • + +
    tool
    +
    #icon-gongju
    +
  • + +
  • + +
    move
    +
    #icon-yidong
    +
  • + +
  • + +
    rotating
    +
    #icon-xuanzhuan
    +
  • + +
  • + +
    mapping
    +
    #icon-yingshe
    +
  • + +
  • + +
    information
    +
    #icon-xinxi
    +
  • + +
  • + +
    According to
    +
    #icon-xianshi
    +
  • + +
  • + +
    submit
    +
    #icon-tijiao
    +
  • + +
  • + +
    hang up
    +
    #icon-guaqi
    +
  • + +
  • + +
    Exit full screen
    +
    #icon-tuichuquanping
    +
  • + +
  • + +
    Full screen
    +
    #icon-a-Fullscreen
    +
  • + +
  • + +
    help
    +
    #icon-help
    +
  • + +
  • + +
    save
    +
    #icon-save
    +
  • + +
  • + +
    Work flow
    +
    #icon-a-Workflow
    +
  • + +
  • + +
    Expiration time
    +
    #icon-a-lujing15750
    +
  • + +
  • + +
    Job information
    +
    #icon-a-Jobinformation
    +
  • + +
+
+

Symbol 引用

+
+ +

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 + 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:

+
    +
  • 支持多色图标了,不再受单色限制。
  • +
  • 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
  • +
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • +
  • 浏览器渲染 SVG 的性能一般,还不如 png。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 symbol 代码:

+
<script src="./iconfont.js"></script>
 
-

第二步:加入通用 CSS 代码(引入一次就行):

-
<style>
+          

第二步:加入通用 CSS 代码(引入一次就行):

+
<style>
 .icon {
   width: 1em;
   height: 1em;
@@ -1249,33 +2662,34 @@ 

第二步:加入通用 CSS 代码(引入一次就行):

-

第三步:挑选相应图标并获取类名,应用于页面:

-
<svg class="icon" aria-hidden="true">
+          

第三步:挑选相应图标并获取类名,应用于页面:

+
<svg class="icon" aria-hidden="true">
   <use xlink:href="#icon-xxx"></use>
 </svg>
 
-
-
-
-
- - +
+
+ +
+
+ + diff --git a/frontend/pc-tool/public/iconfont/iconfont.css b/frontend/pc-tool/public/iconfont/iconfont.css index 8cf55f26..a409be59 100644 --- a/frontend/pc-tool/public/iconfont/iconfont.css +++ b/frontend/pc-tool/public/iconfont/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 3295834 */ - src: url('iconfont.woff2?t=1659510737631') format('woff2'), - url('iconfont.woff?t=1659510737631') format('woff'), - url('iconfont.ttf?t=1659510737631') format('truetype'); + src: url('iconfont.woff2?t=1693477727245') format('woff2'), + url('iconfont.woff?t=1693477727245') format('woff'), + url('iconfont.ttf?t=1693477727245') format('truetype'); } .iconfont { @@ -13,6 +13,230 @@ -moz-osx-font-smoothing: grayscale; } +.icon-label:before { + content: "\e76f"; +} + +.icon-polyline-v1:before { + content: "\e602"; +} + +.icon-polygon-v1:before { + content: "\e601"; +} + +.icon-ruler:before { + content: "\e6a7"; +} + +.icon-lunkuo:before { + content: "\e76d"; +} + +.icon-jiance:before { + content: "\e76e"; +} + +.icon-Hotkeys:before { + content: "\e767"; +} + +.icon-rect1:before { + content: "\e67c"; +} + +.icon-bone:before { + content: "\e68e"; +} + +.icon-splinecurve:before { + content: "\e698"; +} + +.icon-polygon1:before { + content: "\e69b"; +} + +.icon-unclosedpolygon:before { + content: "\e6a3"; +} + +.icon-bezier-curve:before { + content: "\e7ab"; +} + +.icon-point:before { + content: "\e76c"; +} + +.icon-liebiao:before { + content: "\e768"; +} + +.icon-a-Cropnon-firstobject:before { + content: "\e769"; +} + +.icon-a-Cropthefirstobject:before { + content: "\e76a"; +} + +.icon-tucengxianshi:before { + content: "\e76b"; +} + +.icon-group:before { + content: "\e762"; +} + +.icon-tuichuzu:before { + content: "\e763"; +} + +.icon-ai1:before { + content: "\e764"; +} + +.icon-cancel:before { + content: "\e765"; +} + +.icon-jiaohushi:before { + content: "\e766"; +} + +.icon-add:before { + content: "\e758"; +} + +.icon-more:before { + content: "\e759"; +} + +.icon-error:before { + content: "\e75a"; +} + +.icon-fangda:before { + content: "\e75b"; +} + +.icon-cube:before { + content: "\e75c"; +} + +.icon-a-Noresultinthisframe:before { + content: "\e75d"; +} + +.icon-a-aikuang:before { + content: "\e75e"; +} + +.icon-noun-isometric:before { + content: "\e75f"; +} + +.icon-a-GroundTruthinallframes:before { + content: "\e760"; +} + +.icon-a-Groundtruth:before { + content: "\e761"; +} + +.icon-huifu:before { + content: "\e757"; +} + +.icon-Acceptance:before { + content: "\e752"; +} + +.icon-review1:before { + content: "\e753"; +} + +.icon-Annotate:before { + content: "\e754"; +} + +.icon-Modify:before { + content: "\e755"; +} + +.icon-QA:before { + content: "\e756"; +} + +.icon-Frame3:before { + content: "\e751"; +} + +.icon-Frame2:before { + content: "\e74f"; +} + +.icon-Frame23:before { + content: "\e750"; +} + +.icon-Classfication:before { + content: "\e74b"; +} + +.icon-Comments:before { + content: "\e74c"; +} + +.icon-Results:before { + content: "\e74d"; +} + +.icon-Validity:before { + content: "\e74e"; +} + +.icon-Group:before { + content: "\e74a"; +} + +.icon-Frame1:before { + content: "\e748"; +} + +.icon-rectangular:before { + content: "\e747"; +} + +.icon-a-3d:before { + content: "\e749"; +} + +.icon-bofang:before { + content: "\e741"; +} + +.icon-hebing:before { + content: "\e742"; +} + +.icon-chongxinbofang:before { + content: "\e743"; +} + +.icon-jiancha:before { + content: "\e744"; +} + +.icon-zuo-fuzhi:before { + content: "\e745"; +} + +.icon-right-fuzhi:before { + content: "\e746"; +} + .icon-file:before { content: "\e73c"; } diff --git a/frontend/pc-tool/public/iconfont/iconfont.js b/frontend/pc-tool/public/iconfont/iconfont.js index 822823ca..fcf6796d 100644 --- a/frontend/pc-tool/public/iconfont/iconfont.js +++ b/frontend/pc-tool/public/iconfont/iconfont.js @@ -1,65 +1 @@ -!(function (a) { - var l, - h, - o, - t, - i, - z = - '', - m = (m = document.getElementsByTagName('script'))[m.length - 1].getAttribute( - 'data-injectcss', - ), - v = function (a, l) { - l.parentNode.insertBefore(a, l); - }; - if (m && !a.__iconfont__svg__cssinject__) { - a.__iconfont__svg__cssinject__ = !0; - try { - document.write( - '', - ); - } catch (a) { - console && console.log(a); - } - } - function c() { - i || ((i = !0), o()); - } - function d() { - try { - t.documentElement.doScroll('left'); - } catch (a) { - return void setTimeout(d, 50); - } - c(); - } - (l = function () { - var a, - l = document.createElement('div'); - (l.innerHTML = z), - (z = null), - (l = l.getElementsByTagName('svg')[0]) && - (l.setAttribute('aria-hidden', 'true'), - (l.style.position = 'absolute'), - (l.style.width = 0), - (l.style.height = 0), - (l.style.overflow = 'hidden'), - (l = l), - (a = document.body).firstChild ? v(l, a.firstChild) : a.appendChild(l)); - }), - document.addEventListener - ? ~['complete', 'loaded', 'interactive'].indexOf(document.readyState) - ? setTimeout(l, 0) - : ((h = function () { - document.removeEventListener('DOMContentLoaded', h, !1), l(); - }), - document.addEventListener('DOMContentLoaded', h, !1)) - : document.attachEvent && - ((o = l), - (t = a.document), - (i = !1), - d(), - (t.onreadystatechange = function () { - 'complete' == t.readyState && ((t.onreadystatechange = null), c()); - })); -})(window); +window._iconfont_svg_string_3295834='',function(h){var a=(a=document.getElementsByTagName("script"))[a.length-1],l=a.getAttribute("data-injectcss"),a=a.getAttribute("data-disable-injectsvg");if(!a){var c,z,o,v,m,t=function(a,l){l.parentNode.insertBefore(a,l)};if(l&&!h.__iconfont__svg__cssinject__){h.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}c=function(){var a,l=document.createElement("div");l.innerHTML=h._iconfont_svg_string_3295834,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?t(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(c,0):(z=function(){document.removeEventListener("DOMContentLoaded",z,!1),c()},document.addEventListener("DOMContentLoaded",z,!1)):document.attachEvent&&(o=c,v=h.document,m=!1,p(),v.onreadystatechange=function(){"complete"==v.readyState&&(v.onreadystatechange=null,i())})}function i(){m||(m=!0,o())}function p(){try{v.documentElement.doScroll("left")}catch(a){return void setTimeout(p,50)}i()}}(window); \ No newline at end of file diff --git a/frontend/pc-tool/public/iconfont/iconfont.json b/frontend/pc-tool/public/iconfont/iconfont.json index 4efd0922..141ba27f 100644 --- a/frontend/pc-tool/public/iconfont/iconfont.json +++ b/frontend/pc-tool/public/iconfont/iconfont.json @@ -1,10 +1,402 @@ { "id": "3295834", - "name": "3D Point", + "name": "3D Point Cloud", "font_family": "iconfont", "css_prefix_text": "icon-", "description": "点云类工具图标", "glyphs": [ + { + "icon_id": "25069128", + "name": "biaoqian", + "font_class": "label", + "unicode": "e76f", + "unicode_decimal": 59247 + }, + { + "icon_id": "36275575", + "name": "polyline-v1", + "font_class": "polyline-v1", + "unicode": "e602", + "unicode_decimal": 58882 + }, + { + "icon_id": "36275522", + "name": "polygon-v1", + "font_class": "polygon-v1", + "unicode": "e601", + "unicode_decimal": 58881 + }, + { + "icon_id": "3270854", + "name": "ruler", + "font_class": "ruler", + "unicode": "e6a7", + "unicode_decimal": 59047 + }, + { + "icon_id": "34596995", + "name": "lunkuo", + "font_class": "lunkuo", + "unicode": "e76d", + "unicode_decimal": 59245 + }, + { + "icon_id": "34596996", + "name": "jiance", + "font_class": "jiance", + "unicode": "e76e", + "unicode_decimal": 59246 + }, + { + "icon_id": "34059152", + "name": "Hotkeys", + "font_class": "Hotkeys", + "unicode": "e767", + "unicode_decimal": 59239 + }, + { + "icon_id": "7405528", + "name": "rect", + "font_class": "rect1", + "unicode": "e67c", + "unicode_decimal": 59004 + }, + { + "icon_id": "7405851", + "name": "bone", + "font_class": "bone", + "unicode": "e68e", + "unicode_decimal": 59022 + }, + { + "icon_id": "7405861", + "name": "splinecurve", + "font_class": "splinecurve", + "unicode": "e698", + "unicode_decimal": 59032 + }, + { + "icon_id": "7405864", + "name": "polygon", + "font_class": "polygon1", + "unicode": "e69b", + "unicode_decimal": 59035 + }, + { + "icon_id": "7405872", + "name": "unclosedpolygon", + "font_class": "unclosedpolygon", + "unicode": "e6a3", + "unicode_decimal": 59043 + }, + { + "icon_id": "30360434", + "name": "beisaierquxian", + "font_class": "bezier-curve", + "unicode": "e7ab", + "unicode_decimal": 59307 + }, + { + "icon_id": "34058500", + "name": "point", + "font_class": "point", + "unicode": "e76c", + "unicode_decimal": 59244 + }, + { + "icon_id": "34058417", + "name": "liebiao", + "font_class": "liebiao", + "unicode": "e768", + "unicode_decimal": 59240 + }, + { + "icon_id": "34058418", + "name": "Crop non-first object", + "font_class": "a-Cropnon-firstobject", + "unicode": "e769", + "unicode_decimal": 59241 + }, + { + "icon_id": "34058419", + "name": "Crop the first object", + "font_class": "a-Cropthefirstobject", + "unicode": "e76a", + "unicode_decimal": 59242 + }, + { + "icon_id": "34058420", + "name": "图层显示", + "font_class": "tucengxianshi", + "unicode": "e76b", + "unicode_decimal": 59243 + }, + { + "icon_id": "34058411", + "name": "group", + "font_class": "group", + "unicode": "e762", + "unicode_decimal": 59234 + }, + { + "icon_id": "34058412", + "name": "tuichuzu", + "font_class": "tuichuzu", + "unicode": "e763", + "unicode_decimal": 59235 + }, + { + "icon_id": "34058413", + "name": "ai", + "font_class": "ai1", + "unicode": "e764", + "unicode_decimal": 59236 + }, + { + "icon_id": "34058414", + "name": "cancel", + "font_class": "cancel", + "unicode": "e765", + "unicode_decimal": 59237 + }, + { + "icon_id": "34058415", + "name": "jiaohushi", + "font_class": "jiaohushi", + "unicode": "e766", + "unicode_decimal": 59238 + }, + { + "icon_id": "33327126", + "name": "add", + "font_class": "add", + "unicode": "e758", + "unicode_decimal": 59224 + }, + { + "icon_id": "33327127", + "name": "more", + "font_class": "more", + "unicode": "e759", + "unicode_decimal": 59225 + }, + { + "icon_id": "33327128", + "name": "error", + "font_class": "error", + "unicode": "e75a", + "unicode_decimal": 59226 + }, + { + "icon_id": "33327129", + "name": "fangda", + "font_class": "fangda", + "unicode": "e75b", + "unicode_decimal": 59227 + }, + { + "icon_id": "33327130", + "name": "cube", + "font_class": "cube", + "unicode": "e75c", + "unicode_decimal": 59228 + }, + { + "icon_id": "33327131", + "name": "No result in this frame", + "font_class": "a-Noresultinthisframe", + "unicode": "e75d", + "unicode_decimal": 59229 + }, + { + "icon_id": "33327132", + "name": "ai kuang", + "font_class": "a-aikuang", + "unicode": "e75e", + "unicode_decimal": 59230 + }, + { + "icon_id": "33327133", + "name": "noun-isometric", + "font_class": "noun-isometric", + "unicode": "e75f", + "unicode_decimal": 59231 + }, + { + "icon_id": "33327134", + "name": "Ground Truth in all frames", + "font_class": "a-GroundTruthinallframes", + "unicode": "e760", + "unicode_decimal": 59232 + }, + { + "icon_id": "33327135", + "name": "Ground truth", + "font_class": "a-Groundtruth", + "unicode": "e761", + "unicode_decimal": 59233 + }, + { + "icon_id": "33305053", + "name": "restore", + "font_class": "huifu", + "unicode": "e757", + "unicode_decimal": 59223 + }, + { + "icon_id": "33285949", + "name": "Acceptance", + "font_class": "Acceptance", + "unicode": "e752", + "unicode_decimal": 59218 + }, + { + "icon_id": "33285950", + "name": "review", + "font_class": "review1", + "unicode": "e753", + "unicode_decimal": 59219 + }, + { + "icon_id": "33285951", + "name": "Annotate", + "font_class": "Annotate", + "unicode": "e754", + "unicode_decimal": 59220 + }, + { + "icon_id": "33285952", + "name": "Modify", + "font_class": "Modify", + "unicode": "e755", + "unicode_decimal": 59221 + }, + { + "icon_id": "33285953", + "name": "QA", + "font_class": "QA", + "unicode": "e756", + "unicode_decimal": 59222 + }, + { + "icon_id": "33261081", + "name": "arrow", + "font_class": "Frame3", + "unicode": "e751", + "unicode_decimal": 59217 + }, + { + "icon_id": "32608833", + "name": "suspend", + "font_class": "Frame2", + "unicode": "e74f", + "unicode_decimal": 59215 + }, + { + "icon_id": "32608846", + "name": "ongoing", + "font_class": "Frame23", + "unicode": "e750", + "unicode_decimal": 59216 + }, + { + "icon_id": "32487336", + "name": "Classfication", + "font_class": "Classfication", + "unicode": "e74b", + "unicode_decimal": 59211 + }, + { + "icon_id": "32487337", + "name": "Comments", + "font_class": "Comments", + "unicode": "e74c", + "unicode_decimal": 59212 + }, + { + "icon_id": "32487338", + "name": "Results", + "font_class": "Results", + "unicode": "e74d", + "unicode_decimal": 59213 + }, + { + "icon_id": "32487339", + "name": "Validity", + "font_class": "Validity", + "unicode": "e74e", + "unicode_decimal": 59214 + }, + { + "icon_id": "32437132", + "name": "Group", + "font_class": "Group", + "unicode": "e74a", + "unicode_decimal": 59210 + }, + { + "icon_id": "32410227", + "name": "abnormal", + "font_class": "Frame1", + "unicode": "e748", + "unicode_decimal": 59208 + }, + { + "icon_id": "32271954", + "name": "rectangular", + "font_class": "rectangular", + "unicode": "e747", + "unicode_decimal": 59207 + }, + { + "icon_id": "32271725", + "name": "3d", + "font_class": "a-3d", + "unicode": "e749", + "unicode_decimal": 59209 + }, + { + "icon_id": "32271488", + "name": "bofang", + "font_class": "bofang", + "unicode": "e741", + "unicode_decimal": 59201 + }, + { + "icon_id": "32271489", + "name": "hebing", + "font_class": "hebing", + "unicode": "e742", + "unicode_decimal": 59202 + }, + { + "icon_id": "32271490", + "name": "chongxinbofang", + "font_class": "chongxinbofang", + "unicode": "e743", + "unicode_decimal": 59203 + }, + { + "icon_id": "32271491", + "name": "jiancha", + "font_class": "jiancha", + "unicode": "e744", + "unicode_decimal": 59204 + }, + { + "icon_id": "32271492", + "name": "zuo-fuzhi", + "font_class": "zuo-fuzhi", + "unicode": "e745", + "unicode_decimal": 59205 + }, + { + "icon_id": "32271493", + "name": "right-fuzhi", + "font_class": "right-fuzhi", + "unicode": "e746", + "unicode_decimal": 59206 + }, { "icon_id": "31026982", "name": "file", diff --git a/frontend/pc-tool/public/iconfont/iconfont.ttf b/frontend/pc-tool/public/iconfont/iconfont.ttf index 5fe6908b8df3eab09ca2b4cdac0473721ffe50d0..a19b7c7b2b01e6d9ec603959466df12e3670cac5 100644 GIT binary patch delta 19719 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 V(`Kf2?LO>e9UMX+N*DOM5^RdEt%6a{r* zD)q5JG?j(KvJ#b`QWr=dR!D3jBv>F8ED#_H5%Ljgg-|7`E|A^-_-4b5KF`dZd+#~t z|IhfrFV3~^%yM6C=fO_^a~{CS@oHuMiGSf3;H&`NuTwK;Pdz+4w+{$D1a_^To~%st z|M>EE&V5db(;SElh6235Mf#_!3rjDet-rY6-+X>FGdEsYJT&z=*H7?ywpv-5H)S)x z_gOyo&Q_|EwfN=IHDK%wgHFxQeY0?TvKa#QY7)m_0QX$$9FB*+Y`ts!6D;ek#TgQI$*SFZgwSayHrV+coX7UqKn?NgW;8aWGdL?druwrKQNm^T_ti|!)~qp%hT zy--*c#I>-#8iXDyEEU2H6c!C(aE0YV7*L@Q5C&8z2_#~nI1rYhP$r0PpJFhI3LSt@6@^|vsEUGKUa5*ge;{knDaac1 z3{uk{Li-?V&`5{|B(xNw0SV28Xb?i1A(||q;SjBq(0YifLD&QkRfDh{AgUN)V?b0% zQe%%mRARzzfvEI^eFITT2s;QeVqq^q)LWt-f)p+6F9`o$6zwS4ZDG$rKDFrgD4$u_ zhLAD!e~b(0zmzQYd&7B?H;bm}PPqSie|RrDRsuT$tHA@I$Dw9tv2(WbdDsh&gy+Nm zMHV7AqJH%1x~KlBSRu9)yA!_|Z+6XgJxg>a%88}KZ;91pA$cZwKeaoJ^o4Y@dpT3c fT*_v1rQAIy?_dA=iz`J_tS7#oF}Uvibl3b3kSzuQ diff --git a/frontend/pc-tool/public/iconfont/iconfont.woff b/frontend/pc-tool/public/iconfont/iconfont.woff index 6bab9f3b5c70a6677ffb553e19b6416013eed17b..257b1fc5c613471dff56927a228b98d6af43983e 100644 GIT binary patch delta 22306 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 N>HG`juYLOf0022_-;)3U delta 11017 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 DW(-cF diff --git a/frontend/pc-tool/public/iconfont/iconfont.woff2 b/frontend/pc-tool/public/iconfont/iconfont.woff2 index af6a8470cde837f5742abe952c1ba3cb9f93fa34..f4e181c3c3081bbec177177ccedd0948fc8b4cfd 100644 GIT binary patch literal 19256 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 { let bsObj = object as any; diff --git a/frontend/pc-tool/src/components/EditClass/useEditClass.ts b/frontend/pc-tool/src/components/EditClass/useEditClass.ts index 7f30959c..19ac3d60 100644 --- a/frontend/pc-tool/src/components/EditClass/useEditClass.ts +++ b/frontend/pc-tool/src/components/EditClass/useEditClass.ts @@ -339,6 +339,7 @@ export default function useEditClass() { // let classConfig = editor.getClassType(state.classType); // let size3D = undefined; + const { isSeriesFrame, frameIndex, frames } = editor.state; let classConfig = editor.getClassType(state.classType); let userData = { classType: classConfig?.name, @@ -347,10 +348,18 @@ export default function useEditClass() { resultStatus: Const.True_Value, } as IUserData; - editor.cmdManager.execute('update-object-user-data', { - objects: tempObjects, + editor.cmdManager.withGroup(() => { + editor.trackManager.setTrackData(state.trackId, { + userData: { classType: userData.classType, classId: userData.classId }, + }); - data: userData, + editor.trackManager.setDataByTrackId( + state.trackId, + { + userData: userData, + }, + isSeriesFrame ? frames : [editor.getCurrentFrame()], + ); }); state.resultStatus = Const.True_Value; diff --git a/frontend/pc-tool/src/components/Editor/main.vue b/frontend/pc-tool/src/components/Editor/main.vue index 74748a71..6e5f44a1 100644 --- a/frontend/pc-tool/src/components/Editor/main.vue +++ b/frontend/pc-tool/src/components/Editor/main.vue @@ -11,6 +11,9 @@ +
+ +
@@ -20,6 +23,10 @@ import ImgView from '../ImgView/index.vue'; import SideView from '../SideView/index.vue'; import EditClass from '../EditClass/bs/main.vue'; + import TimeLine from '../TimeLine/index.vue'; + import { useInjectEditor } from '../../state'; + const editor = useInjectEditor(); + const { state } = editor; diff --git a/frontend/pc-tool/src/components/TimeLine/tickLine.vue b/frontend/pc-tool/src/components/TimeLine/tickLine.vue new file mode 100644 index 00000000..c6f4cdc6 --- /dev/null +++ b/frontend/pc-tool/src/components/TimeLine/tickLine.vue @@ -0,0 +1,411 @@ + + + diff --git a/frontend/pc-tool/src/components/TimeLine/toolbar.vue b/frontend/pc-tool/src/components/TimeLine/toolbar.vue new file mode 100644 index 00000000..f56d5d91 --- /dev/null +++ b/frontend/pc-tool/src/components/TimeLine/toolbar.vue @@ -0,0 +1,463 @@ + + + diff --git a/frontend/pc-tool/src/components/TimeLine/trackLine.vue b/frontend/pc-tool/src/components/TimeLine/trackLine.vue new file mode 100644 index 00000000..b98120bd --- /dev/null +++ b/frontend/pc-tool/src/components/TimeLine/trackLine.vue @@ -0,0 +1,381 @@ + + + diff --git a/frontend/pc-tool/src/components/TimeLine/useTimeLine.ts b/frontend/pc-tool/src/components/TimeLine/useTimeLine.ts new file mode 100644 index 00000000..13ea9b11 --- /dev/null +++ b/frontend/pc-tool/src/components/TimeLine/useTimeLine.ts @@ -0,0 +1,616 @@ +import { reactive, onMounted, onBeforeUnmount, watch, ref } from 'vue'; +import { Event as EditorEvent, Const } from 'pc-editor'; +import { IUserData } from 'pc-editor'; +import * as THREE from 'three'; +import * as _ from 'lodash'; +// import ToolEvent from '../../config/event'; +import { useInjectEditor } from '../../state'; + +const COLOR = new THREE.Color(); + +export interface IConfig { + noModelTrack?: boolean; +} + +export type ITrackAction = + | 'Split' + | 'Delete' + | 'MergeTo' + | 'MergeFrom' + | 'PreSplit' + | 'PreMergeTo' + | 'PreMergeFrom' + | 'Cancel' + | ''; +export interface ITrackObject { + trackId: string; + trackName: string; + list: IUserData[]; +} +export type IMsgOption = { + target: { + x: number; + y: number; + }; + data: { + msg: { class: string; msg: string }[]; + }; + visible: boolean; +}; + +export interface IBottomState { + tip: (option: IMsgOption) => void; + _config: IConfig; + colorMap: {}; + // play + playSpeed: number; + playStart: number; + play: boolean; + animation: number; + trackSplitIndex: number; + annotationStatus: boolean[]; + showAnnotation: boolean; + + trackTargetLine: ITrackObject; + + trackList: ITrackObject[]; + + trackMergeCode: string; + trackSplitClass: string; + trackSplitTrackId: string; + trackMergeErrFrame: number[]; // 合并冲突帧 + trackPinSelected: string; // + + trackMergeResult: ITrackObject; + trackAction: ITrackAction; // 操作行为 + + activeType: { + classType: any; + modelClass: any; + }; + frameConfig: { + curFrameIndex: number; + interval: number; + spanWidth: number; + // showProcess: boolean; + }; +} +export default function useBottom() { + const editor = useInjectEditor(); + const zoomContainer = ref(); + const iState = reactive({ + tip: async (option: IMsgOption) => {}, + _config: {}, + colorMap: {}, + playSpeed: 1, + playStart: 0, + play: false, + animation: 1, + // trackFlag: true, + trackList: [], + annotationStatus: [], + showAnnotation: false, + // trackPinList: [], + trackSplitIndex: -1, + trackSplitClass: '', + trackSplitTrackId: '', + trackTargetLine: { trackId: '', trackName: '', list: [] }, + trackMergeResult: { trackId: '', trackName: '', list: [] }, + trackMergeCode: 'ok', + // 合并冲突帧 + trackMergeErrFrame: [], + // 操作行为 + trackAction: '', + trackPinSelected: '', + activeType: { + classType: undefined as any, + modelClass: undefined as any, + }, + frameConfig: { + curFrameIndex: editor.state.frameIndex + 1, + interval: 5, // default 5 + spanWidth: 18, + // showProcess: false, + }, + }); + //@ts-ignore + window.iSState = iState; + watch( + () => editor.state.classTypes, + (classTypes) => { + const colorMap = {}; + classTypes.forEach((item) => { + colorMap[item.id] = item.color; + colorMap[`false_${item.id}`] = `#${COLOR.set(item.color).getHexString()}aa`; + }); + iState.colorMap = colorMap; + }, + { immediate: true }, + ); + + watch( + () => editor.state.frameIndex, + () => { + iState.frameConfig.curFrameIndex = editor.state.frameIndex + 1; + }, + ); + watch( + () => editor.state.frames, + () => { + if (iState.trackTargetLine.list.length !== editor.state.frames.length) { + iState.trackTargetLine = emptyTrackObject(); + } + }, + { + immediate: true, + }, + ); + + onMounted(() => { + editor.addEventListener(EditorEvent.CURRENT_TRACK_CHANGE, onSelect); + editor.playManager.addEventListener(EditorEvent.PLAY_STOP, onFrameStop); + // editor.addEventListener(EditorEvent.PRE_MERGE_ACTION, onPreMergeEvent); + // editor.addEventListener(EditorEvent.PRE_SPLIT_ACTION, onPreSplitEvent); + // editor.addEventListener(EditorEvent.UPDATE_TIME_LINE, onUpdate); + editor.addEventListener(EditorEvent.ANNOTATE_CHANGE, onUpdate); + editor.addEventListener(EditorEvent.ANNOTATE_ADD, onUpdate); + // editor.addEventListener(EditorEvent.ANNOTATE_CLEAR, onUpdate); + editor.addEventListener(EditorEvent.ANNOTATE_TRANSFORM_CHANGE, onUpdate); + // editor.addEventListener(EditorEvent.VALID_CHANGE, onUpdate); + editor.cmdManager.addEventListener(EditorEvent.UNDO, onUpdate); + editor.cmdManager.addEventListener(EditorEvent.REDO, onUpdate); + // editor.addEventListener(EditorEvent.CLEAR_MERGE_SPLIT, onClear); + if (zoomContainer.value) { + const container = zoomContainer.value as HTMLElement; + container.addEventListener('wheel', onMouseWheel); + } + }); + + onBeforeUnmount(() => { + editor.removeEventListener(EditorEvent.CURRENT_TRACK_CHANGE, onSelect); + editor.playManager.removeEventListener(EditorEvent.PLAY_STOP, onFrameStop); + editor.removeEventListener(EditorEvent.ANNOTATE_CHANGE, onUpdate); + editor.removeEventListener(EditorEvent.ANNOTATE_TRANSFORM_CHANGE, onUpdate); + // editor.removeEventListener(EditorEvent.PRE_MERGE_ACTION, onPreMergeEvent); + // editor.removeEventListener(EditorEvent.PRE_SPLIT_ACTION, onPreSplitEvent); + // editor.removeEventListener(EditorEvent.UPDATE_TIME_LINE, onUpdate); + // editor.removeEventListener(EditorEvent.VALID_CHANGE, onUpdate); + editor.removeEventListener(EditorEvent.ANNOTATE_CLEAR, onUpdate); + editor.cmdManager.removeEventListener(EditorEvent.UNDO, onUpdate); + editor.cmdManager.removeEventListener(EditorEvent.REDO, onUpdate); + // editor.removeEventListener(EditorEvent.CLEAR_MERGE_SPLIT, onClear); + if (zoomContainer.value) { + const container = zoomContainer.value as HTMLElement; + container.removeEventListener('wheel', onMouseWheel); + } + }); + + // function updateAnnotationStatus() { + // const trackId = editor.currentTrack; + // const length = editor.state.frames.length; + // const annotations = editor.state.annotations; + // const annotationStatus = Array(length); + // let showAnnotation = false; + + // if (trackId) { + // const objectMap: Record = annotations.reduce((map, obj) => { + // const { data, type, dataId } = obj; + // if (type === 'object') { + // map[data.uuid] = editor.getFrameIndex(dataId); + // } + // return map; + // }, {}); + // const objects = editor.trackManager.getObjects(trackId); + // objects.forEach((item) => { + // if (item) { + // if (objectMap.hasOwnProperty(item.uuid)) { + // annotationStatus[objectMap[item.uuid]] = true; + // showAnnotation = true; + // } + // } + // }); + // } else { + // annotations.forEach((item) => { + // annotationStatus[editor.getFrameIndex(item.dataId)] = true; + // }); + // showAnnotation = annotations.length > 0; + // } + // iState.annotationStatus = annotationStatus; + // iState.showAnnotation = showAnnotation; + // } + + function onFrameStop() { + iState.play = false; + editor.loadManager.loadFrame(editor.state.frameIndex, false, true); + } + + // ------ + // 时间轴 缩放 + function onMouseWheel(event: WheelEvent) { + // return; + + event.preventDefault(); + + if (event.deltaY < 0) { + if (++iState.frameConfig.spanWidth > 36) { + iState.frameConfig.spanWidth = 36; + } + } else { + if (--iState.frameConfig.spanWidth < 14) { + iState.frameConfig.spanWidth = 14; + } + } + + iState.frameConfig.interval = iState.frameConfig.spanWidth < 18 ? 10 : 5; + } + + function onPreMergeEvent(data: any) { + const { trackId, action, trackName } = data.data; + + if (action === 'cancel') { + onHandleTrackAction('Cancel'); + return; + } + + if (!trackId || !action) { + return; + } + Object.assign(iState.trackMergeResult, { + trackId: trackId, + trackName: trackName, + }); + switch (action) { + case 'merge-to': + onHandleTrackAction('PreMergeTo'); + break; + case 'merge-from': + onHandleTrackAction('PreMergeFrom'); + break; + default: + break; + } + } + + function onPreSplitEvent(data: any) { + const { classId, action, trackId } = data.data; + if (action === 'cancel') { + onHandleTrackAction('Cancel'); + } else if (action === 'split') { + iState.trackSplitClass = classId; + iState.trackSplitTrackId = trackId; + onHandleTrackAction('PreSplit'); + } + } + + // function setInvisibleFlag(frameIndex: number, invisible: boolean) { + // const trackId = iState.trackTargetLine.trackId; + // if (!trackId) return; + // editor.toggleInvisible(trackId, invisible); + // updateTrackLine(); + // } + + function onPreTrackAction(action: ITrackAction) { + const trackTargetId = iState.trackTargetLine.trackId; + const { frames, frameIndex } = editor.state; + if (!trackTargetId) { + onClear(); + + editor.showMsg('warning', editor.lang('selectObject')); + + return; + } + + switch (action) { + case 'PreSplit': + iState.trackSplitIndex = frameIndex; + onPreSplitData(); + break; + case 'PreMergeTo': + case 'PreMergeFrom': + if (!iState.trackMergeResult.trackId) { + onClear(); + // tool.editor.showMsg('warning', 'Please chose track object trackId'); + } else { + onPreMergeData(); + } + break; + default: + break; + } + } + + const onHandleTrackAction = _.debounce((action: ITrackAction) => { + iState.trackAction = action; + switch (action) { + case 'Cancel': + onClear(); + break; + case 'PreSplit': + case 'PreMergeFrom': + case 'PreMergeTo': + onPreTrackAction(action); + break; + case 'MergeTo': + case 'MergeFrom': + // onMerge(); + break; + case 'Split': + // onSplit(); + break; + case 'Delete': + onDelete(); + break; + default: + case '': + break; + } + }, 100); + function onDelete() { + const trackTargetId = iState.trackTargetLine.trackId; + if (!trackTargetId) { + editor.showMsg('warning', editor.lang('warnNoObject')); + return; + } + const { frames } = editor.state; + editor + .showConfirm({ + title: editor.lang('btnDelete'), + subTitle: editor.lang('deleteTitle'), + okText: editor.lang('btnDelete'), + cancelText: editor.lang('btnCancelText'), + okDanger: true, + }) + .then( + () => { + try { + editor.trackManager.deleteObjectByTrack(trackTargetId, frames); + editor.pc.selectObject(editor.pc.selection); + editor.showMsg('success', editor.lang('successDelete')); + onClear(); + } catch (error) { + editor.showMsg('error', editor.lang('errorDelete')); + } + }, + () => {}, + ); + } + function updateMergeCodeMsg() { + const trackIdMerge = iState.trackMergeResult.trackId; + + const trackId = iState.trackTargetLine.trackId; + const { code, data } = editor.trackManager.canMerge(trackId, trackIdMerge); + + iState.trackMergeCode = code; + + if (code === 'object_repeat') { + iState.trackMergeErrFrame = data || []; + } else { + iState.trackMergeErrFrame = []; + } + } + // 合并预览 + function onPreMergeData() { + const trackIdMerge = iState.trackMergeResult.trackId; + const trackName = iState.trackMergeResult.trackName; + const trackId = iState.trackTargetLine.trackId; + + if (!trackId || !trackIdMerge) return; + + updateMergeCodeMsg(); + + const trackList: any[] = []; + + // const frameCount = tool.state.dataList.length; + + const mergeTargetTrackList: IUserData[] = getTrackLine(trackIdMerge); + + trackList.push({ + trackName: trackName, + trackId: trackIdMerge, + list: mergeTargetTrackList, + }); + + iState.trackList = trackList; + } + // 拆分预览 + function onPreSplitData() { + const trackId = iState.trackTargetLine.trackId; + const list = iState.trackTargetLine.list; + const dataIndex = iState.trackSplitIndex; + if (!trackId || dataIndex < 0) return; + const frameCount = list.length; + if (!iState.trackSplitTrackId) { + iState.trackSplitTrackId = editor.createTrackId(); + } + const trackIdNew = iState.trackSplitTrackId; + const trackListMap: ITrackObject[] = []; + const beforeList = Array(frameCount); + const afterList = Array(frameCount); + const classType = iState.trackSplitClass; + list.forEach((item, index) => { + if (!item) return; + if (index < dataIndex) { + beforeList[index] = item; + } else { + afterList[index] = { ...item, classType: classType }; + } + }); + trackListMap.push({ + trackName: iState.trackTargetLine.trackName, + trackId: trackId, + list: beforeList, + }); + + trackListMap.push({ + trackName: '', + trackId: trackIdNew, + list: afterList, + }); + + iState.trackList = trackListMap; + } + // 合并 + // function onMerge() { + // const curTrackId = iState.trackTargetLine.trackId; + // const mergeTrackId = iState.trackMergeResult.trackId; + // const trackMergeCode = iState.trackMergeCode; + // if (trackMergeCode !== 'ok') { + // let msg = 'error'; + // switch (trackMergeCode) { + // case 'classType_diff': + // msg = editor.lang('warnClassTypeDiff'); + // break; + // case 'object_repeat': + // msg = editor.lang('warnObjectRepeat'); + // break; + // default: + // break; + // } + // editor.showMsg('warning', msg); + // return; + // } + // try { + // switch (iState.trackAction) { + // case 'MergeFrom': + // editor.trackManager.mergeTrackObject(mergeTrackId, curTrackId); + // updateTrackLine(true); + // break; + + // case 'MergeTo': + // editor.trackManager.mergeTrackObject(curTrackId, mergeTrackId); + // editor.selectByTrackId(mergeTrackId); + // // iState.trackTargetLine.trackId = mergeTrackId; + + // break; + + // default: + // break; + // } + // // editor.dispatchEvent({ type: EditorEvent.CLEAR_MERGE_SPLIT }); + // editor.showMsg('success', editor.lang('successMerge')); + // } catch (error) { + // editor.showMsg('error', editor.lang('errorMerge')); + // } + // } + // 拆分 + // function onSplit() { + // const trackId = iState.trackTargetLine.trackId; + // const start = iState.trackSplitIndex; + + // const canSplit = editor.trackManager.canSplit(trackId, start); + + // if (!canSplit) { + // editor.showMsg('warning', editor.lang('warnEmptyObject')); + // return; + // } + // try { + // const splitTrackId = iState.trackList[1].trackId; + // const classConfig = editor.getClassType(iState.trackSplitClass); + // editor.trackManager.splitTrackObject({ + // trackId: trackId, + // start: start, + // userData: { + // trackId: splitTrackId, + // classId: iState.trackSplitClass, + // classType: classConfig.name, + // }, + // }); + // onClear(); + // updateTrackLine(); + // // editor.dispatchEvent({ type: EditorEvent.CLEAR_MERGE_SPLIT }); + // editor.showMsg('success', editor.lang('successSplit')); + // } catch (error) { + // editor.showMsg('error', editor.lang('errorSplit')); + // } + // } + // 更新当前TrackLine + function updateTrackLine(force: boolean = false) { + const trackId = editor.currentTrack; + if (trackId && trackId === iState.trackTargetLine.trackId && !force) return; + if (trackId) { + Object.assign(iState.trackTargetLine, { + trackId: trackId, + trackName: editor.trackManager.trackMap.get(trackId + '')?.trackName || '', + }); + iState.trackTargetLine.list = getTrackLine(trackId); + } else { + iState.trackTargetLine = emptyTrackObject(); + } + } + + function getTrackLine(trackId: string) { + const length = editor.state.frames.length; + if (!trackId) return Array(length); + const list = editor.trackManager.getTrackObjectMap(trackId)[trackId]; + if (!list) return Array(length); + return list.map((item: any) => { + const invalid = item.some((el: any) => el.invalidConfig); + const trueValue = item.every( + (el: any) => el.userData.resultStatus === Const.True_Value, + ); + return { + ...item[0].userData, + invalid: invalid, + trueValue: trueValue, + }; + }); + } + + // Object userData change Event + const onUpdate = _.throttle((data: any) => { + updateTrackLine(true); + const trackIds = [ + ...iState.trackList.map((item) => item.trackId), + iState.trackMergeResult.trackId, + ]; + if (editor.currentTrack && trackIds.indexOf(editor.currentTrack) < 0) return; + const { trackAction } = iState; + switch (trackAction) { + case 'PreMergeFrom': + case 'PreMergeTo': + updateMergeCodeMsg(); + // falls through + case 'PreSplit': + onPreTrackAction(trackAction); + break; + } + }, 200); + + function emptyTrackObject(): ITrackObject { + const length = editor.state.frames.length; + return { + trackId: '', + trackName: '', + list: Array(length), + }; + } + // object select event + const onSelect = _.debounce(() => { + onClear(); + updateTrackLine(); + // updateAnnotationStatus(); + }, 200); + // 重置 + function onClear() { + iState.trackAction = ''; + iState.trackMergeErrFrame = []; + iState.trackMergeResult = emptyTrackObject(); + iState.trackList = []; + iState.trackSplitClass = ''; + iState.trackSplitTrackId = ''; + // iState.trackMergeResultList = []; + iState.trackSplitIndex = -1; + } + + function setConfig(config?: IConfig) { + Object.assign(iState._config, config || {}); + } + return { + editor, + iState, + setConfig, + zoomContainer, + updateTrackLine, + onHandleTrackAction, + }; +} diff --git a/frontend/pc-tool/src/config/action.ts b/frontend/pc-tool/src/config/action.ts index 76b56683..4b02c101 100644 --- a/frontend/pc-tool/src/config/action.ts +++ b/frontend/pc-tool/src/config/action.ts @@ -27,6 +27,8 @@ export const sideActions: IBsActionName[] = [ export const generalActions: IBsActionName[] = [ 'nextFrame', 'preFrame', + 'copyForward', + 'copyBackWard', 'toggleClassView', 'toggleShowAnnotation', 'toggleShowLabel', diff --git a/frontend/pc-tool/src/hook/useTool.ts b/frontend/pc-tool/src/hook/useTool.ts index 05dea126..27c714a4 100644 --- a/frontend/pc-tool/src/hook/useTool.ts +++ b/frontend/pc-tool/src/hook/useTool.ts @@ -41,8 +41,10 @@ export default function useTool() { async function loadRecord() { try { - let { dataInfos, seriesFrameId } = await api.getInfoByRecordId(bsState.recordId); - // state.isSeriesFrame = isSeriesFrame; + let { dataInfos, seriesFrameId, isSeriesFrame } = await api.getInfoByRecordId( + bsState.recordId, + ); + state.isSeriesFrame = isSeriesFrame; bsState.seriesFrameId = seriesFrameId; let dataId = bsState.query.dataId; if (dataId) { diff --git a/frontend/pc-tool/src/packages/pc-editor/Editor.ts b/frontend/pc-tool/src/packages/pc-editor/Editor.ts index a2cf4095..b1c6df95 100644 --- a/frontend/pc-tool/src/packages/pc-editor/Editor.ts +++ b/frontend/pc-tool/src/packages/pc-editor/Editor.ts @@ -17,6 +17,8 @@ import BusinessManager from './common/BusinessManager'; import LoadManager from './common/LoadManager'; import DataResource from './common/DataResource'; import ModelManager from './common/ModelManager'; +import PlayManager from './common/PlayManager'; +import TrackManager from './common/TrackManager'; import { getDefaultState } from './state'; import type { IState } from './state'; @@ -48,7 +50,9 @@ export default class Editor extends THREE.EventDispatcher { idCount: number = 1; pc: PointCloud; state: IState; - // currentTrack: string | null = null; + currentTrack?: string; + currentTrackName: string = ''; + currentClass: string = ''; frameMap: Map = new Map(); frameIndexMap: Map = new Map(); classMap: Map = new Map(); @@ -62,11 +66,11 @@ export default class Editor extends THREE.EventDispatcher { configManager: ConfigManager; dataManager: DataManager; businessManager: BusinessManager; - // playManager: PlayManager; + playManager: PlayManager; loadManager: LoadManager; dataResource: DataResource; modelManager: ModelManager; - // trackManager: TrackManager; + trackManager: TrackManager; // ui registerModal: RegisterFn = () => {}; @@ -89,12 +93,12 @@ export default class Editor extends THREE.EventDispatcher { this.viewManager = new ViewManager(this); this.configManager = new ConfigManager(this); this.dataManager = new DataManager(this); - // this.playManager = new PlayManager(this); + this.playManager = new PlayManager(this); this.loadManager = new LoadManager(this); this.dataResource = new DataResource(this); this.businessManager = new BusinessManager(this); this.modelManager = new ModelManager(this); - // this.trackManager = new TrackManager(this); + this.trackManager = new TrackManager(this); handleHack(this); @@ -113,10 +117,16 @@ export default class Editor extends THREE.EventDispatcher { if (config.activeTranslate && box) { this.toggleTranslate(true, box as THREE.Object3D); } - + this.updateTrack(); this.dispatchEvent({ type: Event.ANNOTATE_SELECT, data: { ...data.data } }); }); + this.cmdManager.addEventListener(Event.UNDO, () => { + this.updateTrack(); + }); + this.cmdManager.addEventListener(Event.REDO, () => { + this.updateTrack(); + }); this.pc.addEventListener(RenderEvent.OBJECT_DBLCLICK, (data) => { let object = data.data as AnnotateObject; let trackId = object.userData.trackId; @@ -132,7 +142,23 @@ export default class Editor extends THREE.EventDispatcher { } }); } - + updateTrack() { + const selection = this.pc.selection; + const userData = + selection.length > 0 ? (selection[0].userData as Required) : undefined; + + this.setCurrentTrack( + userData ? userData.trackId : undefined, + userData ? userData.trackName : '', + ); + } + setCurrentTrack(trackId: string | undefined = undefined, trackName: string) { + if (this.currentTrack !== trackId) { + this.currentTrack = trackId; + this.currentTrackName = trackName; + this.dispatchEvent({ type: Event.CURRENT_TRACK_CHANGE, data: this.currentTrack }); + } + } // locale lang(name: T, args?: Record) { return this.getLocale(name, locale, args); diff --git a/frontend/pc-tool/src/packages/pc-editor/common/ActionManager/action/create.ts b/frontend/pc-tool/src/packages/pc-editor/common/ActionManager/action/create.ts index c120ed6c..4e1fb82d 100644 --- a/frontend/pc-tool/src/packages/pc-editor/common/ActionManager/action/create.ts +++ b/frontend/pc-tool/src/packages/pc-editor/common/ActionManager/action/create.ts @@ -46,9 +46,31 @@ export const createObjectWith3 = define({ transform.scale.z = Math.max(0.2, transform.scale.z); // debugger; - let userData = {} as IUserData; + let userData = { + resultStatus: Const.True_Value, + resultType: Const.Dynamic, + } as IUserData; // userData.resultType = Const.Dynamic; // userData.resultStatus = Const.True_Value; + const classConfig = editor.getClassType(editor.state.currentClass); + + if (classConfig) { + userData.classType = classConfig.name; + userData.classId = classConfig.id; + } + if (editor.currentTrack) { + const object3d = editor.pc.getAnnotate3D().find((e) => { + return ( + e instanceof Box && + !(e as any).isHolder && + e.userData.trackId == editor.currentTrack + ); + }); + if (!object3d) { + userData.trackId = editor.currentTrack as string; + userData.trackName = editor.currentTrackName; + } + } let box = editor.createAnnotate3D( transform.position, @@ -60,17 +82,17 @@ export const createObjectWith3 = define({ let trackObject: Partial = { trackId: userData.trackId, trackName: userData.trackName, - // resultType: userData.resultType, - // resultStatus: userData.resultStatus, + classType: userData.classType, + classId: userData.classId, }; editor.state.config.showClassView = true; editor.cmdManager.withGroup(() => { editor.cmdManager.execute('add-object', box); - // if (editor.state.isSeriesFrame) { - // editor.cmdManager.execute('add-track', trackObject); - // } + if (editor.state.isSeriesFrame) { + editor.cmdManager.execute('add-track', trackObject); + } editor.cmdManager.execute('select-object', box); }); diff --git a/frontend/pc-tool/src/packages/pc-editor/common/ActionManager/action/create2D.ts b/frontend/pc-tool/src/packages/pc-editor/common/ActionManager/action/create2D.ts index 7d8f7e8d..1f651393 100644 --- a/frontend/pc-tool/src/packages/pc-editor/common/ActionManager/action/create2D.ts +++ b/frontend/pc-tool/src/packages/pc-editor/common/ActionManager/action/create2D.ts @@ -12,7 +12,7 @@ import * as _ from 'lodash'; import Editor from '../../../Editor'; import { define } from '../define'; import { setIdInfo, clamRange } from '../../../utils'; -import { Const, IAnnotationInfo, IUserData, StatusType } from '../../../type'; +import { Const, IAnnotationInfo, IObject, IUserData, StatusType } from '../../../type'; export const create2DRect = define({ valid(editor: Editor) { @@ -76,17 +76,47 @@ export const create2DRect = define({ size.copy(data[0]).sub(data[1]); size.x = Math.abs(size.x); size.y = Math.abs(size.y); - - let rect = editor.createAnnotateRect(center, size, { + const viewId = view.renderId || view.id; + const viewIndex = parseInt((viewId.match(/[0-9]{1,5}$/) as any)[0]); + const classConfig = editor.getClassType(editor.state.currentClass); + const userData = { resultStatus: Const.True_Value, resultType: Const.Dynamic, - } as IUserData); - let userData = rect.userData; - rect.viewId = view.renderId || view.id; + } as IUserData; + if (classConfig) { + userData.classType = classConfig.name; + userData.classId = classConfig.id; + } + if (editor.currentTrack) { + const object2d = editor.pc.getAnnotate3D().find((e) => { + return ( + e instanceof Rect && + !(e as any).isHolder && + e.userData.viewIndex === viewIndex && + e.userData.trackId == editor.currentTrack + ); + }); + if (!object2d) { + userData.trackId = editor.currentTrack as string; + userData.trackName = editor.currentTrackName; + } + } + let trackObject: Partial = { + trackId: userData.trackId, + trackName: userData.trackName, + classType: userData.classType, + classId: userData.classId, + }; + + let rect = editor.createAnnotateRect(center, size, userData); + rect.viewId = viewId; editor.state.config.showClassView = true; editor.cmdManager.withGroup(() => { editor.cmdManager.execute('add-object', [{ objects: rect }]); + if (editor.state.isSeriesFrame) { + editor.cmdManager.execute('add-track', trackObject); + } editor.cmdManager.execute('select-object', rect); }); @@ -143,20 +173,51 @@ export const create2DBox = define({ let positions1 = getPositionFromPoints([data[0], data[1]]); let positions2 = getPositionFromPoints([data[2], data[3]]); + const viewId = view.renderId || view.id; + const viewIndex = parseInt((viewId.match(/[0-9]{1,5}$/) as any)[0]); + const classConfig = editor.getClassType(editor.state.currentClass); + const userData = { + resultStatus: Const.True_Value, + resultType: Const.Dynamic, + } as IUserData; + if (classConfig) { + userData.classType = classConfig.name; + userData.classId = classConfig.id; + } + if (editor.currentTrack) { + const object2d = editor.pc.getAnnotate3D().find((e) => { + return ( + e instanceof Box2D && + !(e as any).isHolder && + e.userData.viewIndex === viewIndex && + e.userData.trackId == editor.currentTrack + ); + }); + if (!object2d) { + userData.trackId = editor.currentTrack as string; + userData.trackName = editor.currentTrackName; + } + } + let trackObject: Partial = { + trackId: userData.trackId, + trackName: userData.trackName, + classType: userData.classType, + classId: userData.classId, + }; + let box = editor.createAnnotateBox2D( positions1 as any, positions2 as any, - { - resultStatus: Const.True_Value, - resultType: Const.Dynamic, - } as IUserData, + userData, ); - let userData = box.userData; - box.viewId = view.renderId || view.id; + box.viewId = viewId; editor.state.config.showClassView = true; editor.cmdManager.withGroup(() => { editor.cmdManager.execute('add-object', [{ objects: box }]); + if (editor.state.isSeriesFrame) { + editor.cmdManager.execute('add-track', trackObject); + } editor.cmdManager.execute('select-object', box); }); diff --git a/frontend/pc-tool/src/packages/pc-editor/common/ActionManager/action/general.ts b/frontend/pc-tool/src/packages/pc-editor/common/ActionManager/action/general.ts index 56de65f5..f96b7f46 100644 --- a/frontend/pc-tool/src/packages/pc-editor/common/ActionManager/action/general.ts +++ b/frontend/pc-tool/src/packages/pc-editor/common/ActionManager/action/general.ts @@ -51,9 +51,9 @@ export const toggleShowAnnotation = define({ return true; }, execute(editor: Editor) { - let config = editor.state.config; - config.showAnnotation = !config.showAnnotation; - editor.pc.render(); + // let config = editor.state.config; + // config.showAnnotation = !config.showAnnotation; + // editor.pc.render(); }, }); diff --git a/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/AddTrack.ts b/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/AddTrack.ts new file mode 100644 index 00000000..1f618b1c --- /dev/null +++ b/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/AddTrack.ts @@ -0,0 +1,30 @@ +import CmdBase from '../CmdBase'; +import * as _ from 'lodash'; +import type { ICmdOption } from './index'; +import { IObject } from '../../../type'; + +export type IAddTrackOption = Partial | Partial[]; + +export default class AddTrack extends CmdBase { + redo(): void { + const editor = this.editor; + + let data = this.data; + if (!Array.isArray(data)) data = [data]; + + data.forEach((e: Partial) => { + if (!e.trackId) e.trackId = editor.createTrackId(); + editor.trackManager.addTrackObject(e.trackId, e); + }); + } + undo(): void { + const editor = this.editor; + + let data = this.data; + if (!Array.isArray(data)) data = [data]; + + data.forEach((e: Partial) => { + editor.trackManager.removeTrackObject(e.trackId as string); + }); + } +} diff --git a/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/DeleteTrack.ts b/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/DeleteTrack.ts new file mode 100644 index 00000000..1efce3ef --- /dev/null +++ b/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/DeleteTrack.ts @@ -0,0 +1,37 @@ +import CmdBase from '../CmdBase'; +import * as THREE from 'three'; +import * as _ from 'lodash'; +import type { ICmdOption } from './index'; +import { AnnotateObject } from 'pc-render'; +import { IObject } from '../../../type'; + +export type IDeleteTrackOption = string | string[]; + +export default class DeleteTrack extends CmdBase { + redo(): void { + const editor = this.editor; + + let data = this.data; + if (!Array.isArray(data)) data = [data]; + + if (!this.undoData) { + this.undoData = data.map((trackId) => + editor.trackManager.getTrackObject(trackId), + ) as IObject[]; + } + + data.forEach((trackId) => { + editor.trackManager.removeTrackObject(trackId); + }); + } + undo(): void { + const editor = this.editor; + + if (!this.undoData) return; + + const data = this.undoData; + data.forEach((obj) => { + editor.trackManager.addTrackObject(obj.trackId as string, obj); + }); + } +} diff --git a/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/UpdateTrackData.ts b/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/UpdateTrackData.ts new file mode 100644 index 00000000..030b5003 --- /dev/null +++ b/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/UpdateTrackData.ts @@ -0,0 +1,49 @@ +import CmdBase from '../CmdBase'; +import * as THREE from 'three'; +import * as _ from 'lodash'; +import type { ICmdOption } from './index'; + +import { ITrackOption } from './UpdateTrackDataBatch'; + +export default class UpdateTrackData extends CmdBase { + redo(): void { + let editor = this.editor; + let { frames } = this.editor.state; + let data = this.data; + + if (!this.undoData) { + let undoData = {} as ICmdOption['update-track-data']; + let oldData = editor.trackManager.getTrackObject(data.trackId) || {}; + oldData = JSON.parse(JSON.stringify(oldData)); + undoData = { ...data, data: oldData }; + this.undoData = undoData; + } + + editor.trackManager.updateTrackData(data.trackId, data.data); + } + undo(): void { + let editor = this.editor; + let { frames } = this.editor.state; + + if (!this.undoData) return; + + let data = this.undoData; + editor.trackManager.updateTrackData(data.trackId, data.data); + } + canMerge(cmd: UpdateTrackData): boolean { + let data = this.data; + let offsetTime = Math.abs(this.updateTime - cmd.updateTime); + let valid = + cmd instanceof UpdateTrackData && + data.trackId === data.trackId && + data.frame === data.frame && + offsetTime < 2000; + + return valid; + } + + merge(cmd: UpdateTrackData) { + Object.assign(this.data.data, cmd.data.data); + this.updateTime = new Date().getTime(); + } +} diff --git a/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/UpdateTrackDataBatch.ts b/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/UpdateTrackDataBatch.ts new file mode 100644 index 00000000..f85e5aef --- /dev/null +++ b/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/UpdateTrackDataBatch.ts @@ -0,0 +1,91 @@ +import CmdBase from '../CmdBase'; +import * as THREE from 'three'; +import * as _ from 'lodash'; +import type { ICmdOption } from './index'; +import { IObject, IFrame } from '../../../type'; +import { ITransform, Box, AnnotateObject } from 'pc-render'; + +export interface ITrackOption { + trackId: string; + frame?: IFrame; + data: Partial; +} + +export interface ITransformOption { + objects: Box[]; + transforms: ITransform | ITransform[]; + // rotation?: THREE.Vector3; +} + +export interface IUpdateTrackBatchOption { + tracks: ITrackOption[]; + objects: AnnotateObject[]; + // transform?: ITransformOption; +} + +export default class UpdateTrackDataBatch extends CmdBase< + ICmdOption['update-track-data-batch'], + ICmdOption['update-track-data-batch'] +> { + redo(): void { + let editor = this.editor; + let { frames } = this.editor.state; + + let { tracks, objects } = this.data; + + if (!this.undoData) { + let undoData: Required = { + tracks: [], + objects: objects, + // transform: { objects: [], transforms: [] }, + }; + tracks.forEach((data) => { + let oldData = editor.trackManager.getTrackObject(data.trackId) || {}; + oldData = JSON.parse(JSON.stringify(oldData)); + undoData.tracks.push({ ...data, data: oldData }); + }); + + // if (transform) { + // let { transforms, objects } = transform; + // undoData.transform.objects = objects; + // transform.objects.forEach((object, index) => { + // let transform = Array.isArray(transforms) ? transforms[index] : transforms; + + // let copyTransform = {} as ITransform; + // if (transform.position) copyTransform.position = object.position.clone(); + // if (transform.scale) copyTransform.scale = object.scale.clone(); + // if (transform.rotation) copyTransform.rotation = object.rotation.clone(); + // (undoData.transform.transforms as ITransform[]).push(copyTransform); + // }); + // } + + this.undoData = undoData; + } + + tracks.forEach((data) => { + editor.trackManager.updateTrackData(data.trackId, data.data); + }); + + // if (transform && transform.objects.length > 0) { + // editor.dataManager.setAnnotatesTransform(transform.objects, transform.transforms); + // } + if (objects.length > 0) this.editor.trackManager.updateObjectRenderInfo(objects); + } + undo(): void { + let editor = this.editor; + let { frames } = this.editor.state; + + if (!this.undoData) return; + + let { tracks, objects } = this.undoData; + + tracks.forEach((data) => { + editor.trackManager.updateTrackData(data.trackId, data.data); + }); + + if (objects.length > 0) this.editor.trackManager.updateObjectRenderInfo(objects); + // if (transform && transform.objects.length > 0) { + // editor.dataManager.setAnnotatesTransform(transform.objects, transform.transforms); + // } + } +} diff --git a/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/UpdateTransformBatch.ts b/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/UpdateTransformBatch.ts new file mode 100644 index 00000000..d499dfea --- /dev/null +++ b/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/UpdateTransformBatch.ts @@ -0,0 +1,41 @@ +import CmdBase from '../CmdBase'; +import * as THREE from 'three'; +import type { ICmdOption } from './index'; +import { ITransform, Box } from 'pc-render'; + +export interface IUpdateTransformBatchOption { + objects: Box[]; + transforms: ITransform | ITransform[]; + // rotation?: THREE.Vector3; +} + +export default class UpdateTransformBatch extends CmdBase< + ICmdOption['update-transform-batch'], + ICmdOption['update-transform-batch'] +> { + redo(): void { + const { objects, transforms } = this.data; + const editor = this.editor; + if (!this.undoData) { + const undoData = { objects: objects, transforms: [] } as IUpdateTransformBatchOption; + objects.forEach((object, index) => { + const transform = Array.isArray(transforms) ? transforms[index] : transforms; + + const copyTransform = {} as ITransform; + if (transform.position) copyTransform.position = object.position.clone(); + if (transform.scale) copyTransform.scale = object.scale.clone(); + if (transform.rotation) copyTransform.rotation = object.rotation.clone(); + (undoData.transforms as ITransform[]).push(copyTransform); + }); + this.undoData = undoData; + } + + editor.dataManager.setAnnotatesTransform(objects, transforms); + } + undo(): void { + if (!this.undoData) return; + const editor = this.editor; + + editor.dataManager.setAnnotatesTransform(this.undoData.objects, this.undoData.transforms); + } +} diff --git a/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/index.ts b/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/index.ts index db039b01..b5901692 100644 --- a/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/index.ts +++ b/frontend/pc-tool/src/packages/pc-editor/common/CmdManager/cmd/index.ts @@ -8,7 +8,14 @@ import Update2DBox from './Update2DBox'; import SelectObject from './SelectObject'; import UpdateObjectDataBatch, { IUpdateObjectUserDataOption } from './UpdateObjectUserData'; import ToggleVisible, { IToggleVisibleOption } from './ToggleVisible'; - +import UpdateTransformBatch, { IUpdateTransformBatchOption } from './UpdateTransformBatch'; +import UpdateTrackDataBatch, { + ITrackOption, + IUpdateTrackBatchOption, +} from './UpdateTrackDataBatch'; +import UpdateTrackData from './UpdateTrackData'; +import DeleteTrack, { IDeleteTrackOption } from './DeleteTrack'; +import AddTrack, { IAddTrackOption } from './AddTrack'; export interface ICmdOption { 'add-object': IAddObjectOption; 'delete-object': IDeleteObjectOption; @@ -30,6 +37,11 @@ export interface ICmdOption { }; 'update-object-user-data': IUpdateObjectUserDataOption; 'toggle-visible': IToggleVisibleOption; + 'update-transform-batch': IUpdateTransformBatchOption; + 'update-track-data-batch': IUpdateTrackBatchOption; + 'update-track-data': ITrackOption; + 'delete-track': IDeleteTrackOption; + 'add-track': IAddTrackOption; } type Name = keyof ICmdOption; @@ -43,6 +55,11 @@ const CMD: Record = { 'update-2d-box': Update2DBox, 'update-object-user-data': UpdateObjectDataBatch, 'toggle-visible': ToggleVisible, + 'update-transform-batch': UpdateTransformBatch, + 'update-track-data-batch': UpdateTrackDataBatch, + 'update-track-data': UpdateTrackData, + 'delete-track': DeleteTrack, + 'add-track': AddTrack, }; export default CMD; diff --git a/frontend/pc-tool/src/packages/pc-editor/common/DataManager.ts b/frontend/pc-tool/src/packages/pc-editor/common/DataManager.ts index bef6eb11..1fa8ff91 100644 --- a/frontend/pc-tool/src/packages/pc-editor/common/DataManager.ts +++ b/frontend/pc-tool/src/packages/pc-editor/common/DataManager.ts @@ -63,6 +63,7 @@ export default class DataManager { if (this.hasObject(e.uuid, frame)) return; allObjects.push(e); this.setHasMap(e.uuid, e, frame); + this.editor.trackManager.updateObjectRenderInfo(e); }); frame.needSave = true; @@ -143,7 +144,14 @@ export default class DataManager { if (frame) frame.needSave = true; let data = Array.isArray(datas) ? datas[index] : datas; + const needUpdateInfo = data.hasOwnProperty('trackId'); + if (needUpdateInfo) { + this.editor.trackManager.removeTrackCount(obj, frame); + } Object.assign(obj.userData, data); + if (needUpdateInfo) { + this.editor.trackManager.addTrackCount(obj, frame); + } }); // this.editor.pc.setObjectUserData(objects,datas); this.onAnnotatesChange(objects, frame, { type: 'userData', datas }); @@ -206,6 +214,7 @@ export default class DataManager { frame.needSave = true; this.editor.pc.render(); console.log('onAnnotatesAdd', { objects, frame }); + this.editor.trackManager.addTrackCount(objects, frame); this.editor.dispatchEvent({ type: Event.ANNOTATE_ADD, data: { objects, frame } }); } @@ -214,6 +223,7 @@ export default class DataManager { frame.needSave = true; this.editor.pc.render(); console.log('onAnnotatesRemove', { objects, frame }); + this.editor.trackManager.removeTrackCount(objects, frame); this.editor.dispatchEvent({ type: Event.ANNOTATE_REMOVE, data: { objects, frame } }); } @@ -399,88 +409,88 @@ export default class DataManager { // trackIdName: Record, // onComplete?: () => void, // ) {} - // copyForward() { - // return this.track({ - // direction: 'FORWARD', - // object: this.editor.pc.selection.length > 0 ? 'select' : 'all', - // method: 'copy', - // frameN: 1, - // }); - // } - // copyBackWard() { - // return this.track({ - // direction: 'BACKWARD', - // object: this.editor.pc.selection.length > 0 ? 'select' : 'all', - // method: 'copy', - // frameN: 1, - // }); - // } - // async track(option: { - // method: 'copy' | 'model'; - // object: 'select' | 'all'; - // direction: 'BACKWARD' | 'FORWARD'; - // frameN: number; - // }) { - // let editor = this.editor; - // let { frameIndex, frames } = editor.state; - // let curId = frames[frameIndex].id; - - // const getToDataId = function getToDataId() { - // let ids = [] as string[]; - // let forward = option.direction === 'FORWARD' ? 1 : -1; - // let frameN = option.frameN; - - // if (frameN > 0) - // for (let i = 1; i <= frameN; i++) { - // let frame = frames[frameIndex + forward * i]; - // if (frame) { - // ids.push(frame.id); - // } - // } - // return ids; - // }; - // const getObjects = function getObjects() { - // let dataId = frames[frameIndex].id; - // let objects = editor.dataManager.getFrameObject(dataId) || []; - - // if (option.object === 'select') { - // objects = editor.pc.selection; - // } + copyForward() { + return this.track({ + direction: 'FORWARD', + object: 'select', + method: 'copy', + frameN: 1, + }); + } + copyBackWard() { + return this.track({ + direction: 'BACKWARD', + object: 'select', + method: 'copy', + frameN: 1, + }); + } + async track(option: { + method: 'copy' | 'model'; + object: 'select' | 'all'; + direction: 'BACKWARD' | 'FORWARD'; + frameN: number; + }) { + let editor = this.editor; + let { frameIndex, frames } = editor.state; + let curId = frames[frameIndex].id; + + const getToDataId = function getToDataId() { + let ids = [] as string[]; + let forward = option.direction === 'FORWARD' ? 1 : -1; + let frameN = option.frameN; + + if (frameN > 0) + for (let i = 1; i <= frameN; i++) { + let frame = frames[frameIndex + forward * i]; + if (frame) { + ids.push(frame.id); + } + } + return ids; + }; + const getObjects = function getObjects() { + let dataId = frames[frameIndex].id; + let objects = editor.dataManager.getFrameObject(dataId) || []; - // objects = objects.filter((object) => { - // return object instanceof Box && !object.userData.invisibleFlag; - // }); - - // return objects as Box[]; - // }; - // let ids = getToDataId(); - // if (ids.length === 0) { - // // editor.showMsg('warning', props.state.$$('warnEmptyTarget')); - // return; - // } - - // let objects = getObjects(); - // if (objects.length === 0) { - // editor.showMsg('warning', editor.lang('track-no-source')); - // return; - // } - - // if (option.method === 'copy') { - // utils.copyData(editor, curId, ids, objects); - // editor.showMsg('success', editor.lang('track-ok')); - // this.gotoNext(ids[0]); - // } else { - // await this.modelTrack(ids, objects, option.direction); - // } - // } - // gotoNext(dataId: string) { - // let { frames } = this.editor.state; - // let index = frames.findIndex((e) => e.id === dataId); - // // index = Math.max(0, Math.min(editor.state.frames.length-1, index)) - // if (index < 0) return; - // this.editor.loadFrame(index); - // this.editor.dispatchEvent({ type: EditorEvent.UPDATE_TIME_LINE }); - // } + if (option.object === 'select') { + objects = editor.pc.selection; + } + + objects = objects.filter((object) => { + return object instanceof Box && !object.userData.invisibleFlag; + }); + + return objects as Box[]; + }; + let ids = getToDataId(); + if (ids.length === 0) { + // editor.showMsg('warning', props.state.$$('warnEmptyTarget')); + return; + } + + let objects = getObjects(); + if (objects.length === 0) { + editor.showMsg('warning', editor.lang('track-no-source')); + return; + } + + if (option.method === 'copy') { + utils.copyData(editor, curId, ids, objects); + editor.showMsg('success', editor.lang('track-ok')); + this.gotoNext(ids[0]); + } else { + // await this.modelTrack(ids, objects, option.direction); + } + } + gotoNext(dataId: string) { + let { frames } = this.editor.state; + let index = frames.findIndex((e) => e.id === dataId); + // index = Math.max(0, Math.min(editor.state.frames.length-1, index)) + if (index < 0) return; + this.editor.loadFrame(index); + // this.editor.dispatchEvent({ type: EditorEvent.UPDATE_TIME_LINE }); + } // async modelTrack( // toIds: string[], // objects: AnnotateObject[], diff --git a/frontend/pc-tool/src/packages/pc-editor/common/DataResource.ts b/frontend/pc-tool/src/packages/pc-editor/common/DataResource.ts index 0aa21edc..1aea91a5 100644 --- a/frontend/pc-tool/src/packages/pc-editor/common/DataResource.ts +++ b/frontend/pc-tool/src/packages/pc-editor/common/DataResource.ts @@ -192,6 +192,7 @@ export default class DataResource { setLoadMode(mode: LoadMode) { this.loadMode = mode; + this.editor.state.config.autoLoad = mode === 'all'; } load(fromIndex?: number) { diff --git a/frontend/pc-tool/src/packages/pc-editor/common/LoadManager.ts b/frontend/pc-tool/src/packages/pc-editor/common/LoadManager.ts index 3612ae76..7b43e40c 100644 --- a/frontend/pc-tool/src/packages/pc-editor/common/LoadManager.ts +++ b/frontend/pc-tool/src/packages/pc-editor/common/LoadManager.ts @@ -14,9 +14,12 @@ export default class LoadManager { } async loadFrame(index: number, showLoading: boolean = true, force: boolean = false) { - if (index === this.editor.state.frameIndex && !force) return; - if (index > this.editor.state.frames.length - 1 || index < 0) return; - // let currentTrack = this.editor.currentTrack; + const { isSeriesFrame, frameIndex, frames } = this.editor.state; + if (index === frameIndex && !force) return; + if (index > frames.length - 1 || index < 0) return; + if (!isSeriesFrame) this.editor.cmdManager.reset(); + const currentTrack = this.editor.currentTrack; + const currentTrackName = this.editor.currentTrackName; this.editor.state.frameIndex = index; @@ -31,11 +34,11 @@ export default class LoadManager { this.editor.handleErr(error); } - // if (currentTrack) this.editor.selectByTrackId(currentTrack); - this.editor.pc.selectObject(); + if (currentTrack) this.editor.selectByTrackId(currentTrack); + else this.editor.pc.selectObject(); showLoading && this.editor.showLoading(false); - // this.editor.setCurrentTrack(currentTrack); + this.editor.setCurrentTrack(currentTrack, currentTrackName); this.editor.dispatchEvent({ type: Event.FRAME_CHANGE, data: this.editor.state.frameIndex }); } @@ -98,7 +101,51 @@ export default class LoadManager { this.editor.updateIDCounter(); // this.editor.pc.addObject(annotates); } - + updateTrackMap(frames?: IFrame[]) { + const { state } = this.editor; + frames = frames || state.frames; + const objectMap: Record = {}; + this.editor.trackManager.trackInfo.clear(); + frames.forEach((frame) => { + const objects = this.editor.dataManager.getFrameObject(frame.id) || []; + this.editor.trackManager.addTrackCount(objects, frame); + objectMap[frame.id] = objects.map((e) => e.userData as IObject); + }); + this.setTrackData(objectMap); + } + setTrackData(objectsMap: Record) { + // update trackId + Object.keys(objectsMap).forEach((frameId) => { + const objects = objectsMap[frameId] || []; + objects.forEach((obj) => { + if (!obj.trackId) obj.trackId = this.editor.createTrackId(); + }); + }); + + const trackInfo = utils.getTrackFromObject(objectsMap); + const objects = Object.keys(trackInfo.globalTrack).map((id) => trackInfo.globalTrack[id]); + // update editor Id + const maxId = getMaxId(objects); + let startId = maxId + 1; + objects.forEach((e) => { + if (!e.trackName) e.trackName = startId++ + ''; + }); + this.editor.idCount = startId; + this.editor.trackManager.trackMap.clear(); + Object.keys(trackInfo.globalTrack).forEach((trackId) => { + this.editor.trackManager.addTrackObject(trackId, trackInfo.globalTrack[trackId]); + }); + + function getMaxId(objects: Partial[]) { + let maxId = 0; + objects.forEach((e) => { + if (!e.trackName) return; + const id = parseInt(e.trackName); + if (id > maxId) maxId = id; + }); + return maxId; + } + } async loadAllClassification() { let { frames, classifications } = this.editor.state; @@ -123,7 +170,7 @@ export default class LoadManager { // SeriesFrame load async loadAllObjects() { - let { frames } = this.editor.state; + let { frames, isSeriesFrame } = this.editor.state; let filterFrames = frames.filter((e) => !this.editor.dataManager.getFrameObject(e.id)); @@ -133,7 +180,7 @@ export default class LoadManager { let data = await this.editor.businessManager.getFrameObject(filterFrames); // let data = await api.getDataObject(dataIds); - // this.setTrackData(data.objectsMap); + if (isSeriesFrame) this.setTrackData(data.objectsMap); filterFrames.forEach((frame) => { let objects = data.objectsMap[frame.id] || []; @@ -144,7 +191,7 @@ export default class LoadManager { let userData = obj.userData as IUserData; if (!userData.id) userData.id = THREE.MathUtils.generateUUID(); }); - + if (isSeriesFrame) this.editor.trackManager.addTrackCount(annotates, frame); this.editor.dataManager.setFrameObject(frame.id, annotates); // this.editor.dataManager.updateFrameId(frame.id); }); diff --git a/frontend/pc-tool/src/packages/pc-editor/common/PlayManager.ts b/frontend/pc-tool/src/packages/pc-editor/common/PlayManager.ts new file mode 100644 index 00000000..f8024797 --- /dev/null +++ b/frontend/pc-tool/src/packages/pc-editor/common/PlayManager.ts @@ -0,0 +1,87 @@ +import Editor from '../Editor'; +import Event from '../config/event'; +import * as THREE from 'three'; +import { StatusType } from 'pc-editor'; + +type IForward = 1 | -1; + +interface IPlayOption { + forward?: IForward; + interval?: number; +} + +export default class PlayManager extends THREE.EventDispatcher { + editor: Editor; + forward: IForward = 1; + interval: number = 300; + playing: boolean = false; + timer: number = -1; + test: number = 1; + constructor(editor: Editor) { + super(); + this.editor = editor; + + this.run = this.run.bind(this); + } + play(option: IPlayOption = {}) { + // let { config } = this.editor.state; + let { forward = 1, interval = this.interval } = option; + + this.dispatchEvent({ type: Event.PLAY_START }); + this.forward = forward; + this.interval = interval; + + if (this.playing) return; + + // this.editor.state.filterActive = [config.FILTER_ALL]; + this.playing = true; + this.editor.state.status = StatusType.Play; + + if (this.timer > 0) { + clearTimeout(this.timer); + } + + this.timer = setTimeout(this.run, this.interval) as any; + } + stop() { + if (!this.playing) return; + + this.playing = false; + this.editor.state.status = StatusType.Default; + + if (this.timer > 0) { + clearTimeout(this.timer); + this.timer = -1; + } + this.dispatchEvent({ type: Event.PLAY_STOP }); + } + async next() { + if (!this.playing) return; + + const { frameIndex, frames } = this.editor.state; + const toIndex = frameIndex + this.forward; + const data = frames[toIndex]; + // data.loadState === 'complete' + if (toIndex < frames.length && toIndex >= 0 && data.loadState === 'complete') { + try { + // console.log('toIndex:', toIndex); + await this.editor.loadFrame(toIndex, false); + // this.dispatchEvent({ type: Event.PLAY_FRAME_CHANGE }); + } catch (error) { + this.editor.handleErr(error as any, this.editor.lang('play-error')); + this.stop(); + } + } else { + this.stop(); + } + } + + async run() { + // console.log('run:'); + await this.next(); + + if (this.playing) { + this.timer = setTimeout(this.run, this.interval) as any; + } + } +} diff --git a/frontend/pc-tool/src/packages/pc-editor/common/TrackManager.ts b/frontend/pc-tool/src/packages/pc-editor/common/TrackManager.ts new file mode 100644 index 00000000..824baf61 --- /dev/null +++ b/frontend/pc-tool/src/packages/pc-editor/common/TrackManager.ts @@ -0,0 +1,498 @@ +import { IFrame, IUserData, ITrackCount, Const, IInfo2D, ObjectType, IBSObject } from '../type'; +import { AnnotateObject, Box, ITransform, Rect, Box2D, Object2D } from 'pc-render'; +import Editor from '../Editor'; +import Event from '../config/event'; +import { IUpdateTrackBatchOption, ITransformOption } from './CmdManager/cmd/UpdateTrackDataBatch'; +import { IUpdateObjectUserDataOption } from './CmdManager/cmd/UpdateObjectUserData'; +import * as utils from '../utils'; +import * as _ from 'lodash'; + +export type IMergeStatus = 'object_repeat' | 'classType_diff' | 'ok'; +export interface IMergeCodeData { + code: IMergeStatus; + data?: any; +} + +export default class TrackManager { + editor: Editor; + // track + trackMap: Map> = new Map(); + + trackInfo: Map = new Map(); + constructor(editor: Editor) { + this.editor = editor; + } + + clear() { + this.trackMap.clear(); + this.trackInfo.clear(); + } + + // basic + hasTrackObject(trackId: string) { + return !!this.getTrackObject(trackId); + } + getTrackObject(trackId: string) { + // let id = frame ? `${frame.id}-${trackId}` : trackId; + // if (frame) return this.frameTrackMap.get(id) as IObject; + return this.trackMap.get(trackId + '') as IUserData; + } + + removeTrackObject(trackId: string) { + return this.trackMap.delete(trackId + ''); + } + addTrackObject(trackId: string, object: Partial) { + // let id = frame ? `${frame.id}-${trackId}` : trackId; + // if (frame) return this.frameTrackMap.set(id, object); + return this.trackMap.set(trackId + '', object); + } + + updateTrackData(trackId: string, object: Partial) { + // console.log(trackId, object, frame); + + const trackObject = this.getTrackObject(trackId); + console.log(trackId, trackObject, object); + Object.assign(trackObject, object || {}); + // this.editor.dispatchEvent({ type: Event.TRACK_OBJECT_CHANGE, data: { trackId } }); + + // this.editor.frameChange(frame); + } + + updateTrackId() { + const trackMap = this.trackMap; + trackMap.forEach((track) => {}); + } + // count + + addTrackCount(objects: AnnotateObject | AnnotateObject[], frame: IFrame) { + const { isSeriesFrame } = this.editor.state; + if (!isSeriesFrame) return; + + if (!Array.isArray(objects)) objects = [objects]; + + objects.forEach((obj) => { + const userData = obj.userData as IUserData; + const trackId = userData.trackId || ''; + if (!this.trackInfo.has(trackId)) { + this.trackInfo.set(trackId, { + object3D: 0, + object2D: [], + count: 0, + }); + } + const info = this.trackInfo.get(trackId) as ITrackCount; + if (obj instanceof Box) { + info.object3D++; + } else { + const viewIndex = userData.viewIndex as number; + const type = obj.objectType; + if (!_.isNumber(viewIndex)) return; + + if (!info.object2D[viewIndex]) info.object2D[viewIndex] = {} as any; + const inf2d = info.object2D[viewIndex] as IInfo2D; + if (!inf2d[type]) inf2d[type] = 0; + inf2d[type]++; + } + + info.count++; + }); + } + + removeTrackCount(objects: AnnotateObject | AnnotateObject[], frame: IFrame) { + const { isSeriesFrame } = this.editor.state; + if (!isSeriesFrame) return; + + if (!Array.isArray(objects)) objects = [objects]; + objects.forEach((obj) => { + const userData = obj.userData as IUserData; + const trackId = userData.trackId || ''; + if (!this.trackInfo.has(trackId)) return; + + const info = this.trackInfo.get(trackId) as ITrackCount; + if (obj instanceof Box) { + info.object3D--; + } else { + const type = obj.objectType; + const viewIndex = userData.viewIndex as number; + + if (!_.isNumber(viewIndex)) return; + const inf2d = info.object2D[viewIndex] as IInfo2D; + if (!inf2d || !(type in inf2d)) return; + + inf2d[type]--; + if (inf2d[type] <= 0) { + delete (inf2d as any)[type]; + } + if (Object.keys(inf2d).length === 0) { + info.object2D[viewIndex] = undefined as any; + } + } + + info.count--; + + if (info.count <= 0) { + this.trackInfo.delete(trackId); + } + }); + } + + // other + getTrackObjectMap(trackId: string | string[]): Record { + const { frames, frameIndex } = this.editor.state; + + const trackObjectMap = {}; + frames.forEach((frame) => { + const trackIdMap = {}; + const objects = this.editor.dataManager.getFrameObject(frame.id) || []; + objects.forEach((item) => { + const trackId = item.userData.trackId; + if (trackId) { + if (!trackIdMap[trackId]) { + trackIdMap[trackId] = []; + } + trackIdMap[trackId].push(item); + } + }); + trackObjectMap[frame.id] = trackIdMap; + }); + + const trackListMap: Record = {}; + const frameCount = frames.length; + const trackIdArray = trackId instanceof Array ? trackId : [trackId]; + frames.forEach((item, frameIndex) => { + const dataId = item.id; + trackIdArray.forEach((trackId: string) => { + const objects = (trackObjectMap[dataId] || {})[trackId]; + if (objects) { + if (!trackListMap[trackId]) { + trackListMap[trackId] = Array(frameCount); + } + trackListMap[trackId][frameIndex] = objects; + } + }); + }); + return trackListMap; + } + splitTrackObject(config: { + trackId: string; + start: number; + end?: number; + userData: IUserData; + }) { + const { frames, frameIndex } = this.editor.state; + + const targetTrackId = config.userData.trackId || this.editor.createTrackId(); + const trackName = this.editor.getId(); + Object.assign(config.userData, { + trackId: targetTrackId, + trackName, + } as IUserData); + + // const objects: AnnotateObject[] = []; + const splitFrames = frames.slice(config.start, config.end); + const splitObject = this.getObjects(config.trackId, splitFrames)[0]; + const splitUserData = splitObject.userData as IUserData; + + const trackObject: Partial = { + trackId: targetTrackId, + trackName, + classType: config.userData.classType, + classId: config.userData.classId, + sourceId: config.userData.sourceId, + }; + + this.editor.cmdManager.withGroup(() => { + this.editor.cmdManager.execute('add-track', trackObject); + this.setDataByTrackId( + config.trackId, + { + userData: config.userData, + }, + splitFrames, + ); + }); + this.editor.selectObject(this.editor.pc.selection); + // this.editor.updateTrack(); + } + canSplit(trackId: string, start?: number, end?: number) { + const objects = this.getTrackObjectMap(trackId)[trackId]; + const frameIndex = typeof start === 'number' ? start : this.editor.state.frameIndex; + end = end || Infinity; + let beforeStatus = false; + let afterStatus = false; + + for (let i = 0; i < objects.length; i++) { + const items = objects[i]; + if (!items || items.length <= 0) continue; + if (i < frameIndex) { + beforeStatus = true; + } else if (i < end) { + afterStatus = true; + } + if (beforeStatus && afterStatus) break; + } + + return beforeStatus && afterStatus; + } + canMerge( + trackId: string, + targetTrackId: string, + frameIndex?: [number, number], + ): IMergeCodeData { + const objectMap = this.getTrackObjectMap([trackId, targetTrackId]); + const trackObjects = objectMap[trackId]; + const targetObjects = objectMap[targetTrackId]; + const errFrameIndex: number[] = []; + + const check = (a: AnnotateObject[], b: AnnotateObject[]) => { + if (!a || !b) return false; + return a.length > 0 && b.length > 0; + }; + + trackObjects.forEach((item, index) => { + if (frameIndex && (frameIndex[0] > index || frameIndex[1] <= index)) return; + if (check(item, targetObjects[index])) { + errFrameIndex.push(index); + } + }); + + if (errFrameIndex.length > 0) { + return { + code: 'object_repeat', + data: errFrameIndex, + }; + } + + const checkClassType = () => { + const aType = (trackObjects.find((item) => !!item) || [])[0]?.userData.classType; + const bType = (targetObjects.find((item) => !!item) || [])[0]?.userData.classType; + return aType === bType; + }; + + if (!checkClassType()) { + return { + code: 'classType_diff', + }; + } + + return { + code: 'ok', + }; + } + mergeTrackObject(trackId: string, targetTrackId: string) { + if (!trackId || !targetTrackId) return; + + const trackObject = this.getTrackObject(targetTrackId); + const trackName = trackObject.trackName; + + this.editor.cmdManager.withGroup(() => { + this.setDataByTrackId(trackId, { + userData: { + trackName: trackName, + trackId: targetTrackId, + }, + }); + + this.editor.cmdManager.execute('delete-track', trackId); + }); + this.editor.selectByTrackId(targetTrackId); + } + + deleteObjectByTrack( + trackId: string | string[], + frames: IFrame[], + validFn: (f: IFrame, e: AnnotateObject) => boolean = () => true, + ) { + const objects = this.getObjects(trackId, frames, validFn); + if (!objects) return; + + const deleteData = [] as { objects: AnnotateObject[]; frame: IFrame }[]; + objects.forEach((obj) => { + // TODO (obj as any).frame + deleteData.push({ objects: [obj], frame: (obj as any).frame as IFrame }); + }); + this.editor.cmdManager.execute('delete-object', deleteData); + // this.editor.dispatchEvent({ type: Event.UPDATE_TIME_LINE }); + } + findTrackResult(trackId: string, objectType?: ObjectType[]) { + let findItem: AnnotateObject | undefined; + const frames = this.editor.state.frames; + const dataManager = this.editor.dataManager; + for (let i = 0; i < frames.length; i++) { + const objects = dataManager.getFrameObject(frames[i].id) || []; + findItem = objects.find((e) => { + if (objectType) { + return e.userData.trackId && objectType.includes(e.objectType); + } + return e.userData.trackId; + }); + } + this.editor.dataManager.dataMap; + return; + } + getObjects( + trackIds: string | string[], + frames?: IFrame[], + filter: (f: IFrame, e: AnnotateObject) => boolean = () => true, + ) { + frames = frames || this.editor.state.frames; + if (!Array.isArray(trackIds)) trackIds = [trackIds]; + + const idMap = {}; + trackIds.forEach((e) => (idMap[e] = true)); + + const findObjects = [] as AnnotateObject[]; + frames.forEach((frame) => { + const objects = this.editor.dataManager.getFrameObject(frame.id) || []; + objects.forEach((obj) => { + if (idMap[obj.userData.trackId] && filter(frame, obj)) findObjects.push(obj); + }); + }); + return findObjects; + } + + // global + setTrackData( + trackIds: string | string[], + option: { + userData?: Pick; + size3D?: THREE.Vector3; + } = {}, + filter: (f: IFrame, e: AnnotateObject) => boolean = () => true, + ) { + if (!this.editor.state.isSeriesFrame) return; + + if (!Array.isArray(trackIds)) trackIds = [trackIds]; + + const objects = this.getObjects(trackIds, undefined, filter); + + this.editor.cmdManager.withGroup(() => { + if (option.userData) { + const options: IUpdateTrackBatchOption = { + tracks: [], + objects: objects, + }; + (trackIds as string[]).forEach((id) => { + options.tracks.push({ trackId: id, data: option.userData as any }); + }); + this.editor.cmdManager.execute('update-track-data-batch', options); + } + + if (option.size3D) { + const object3Ds = objects.filter((e) => e instanceof Box); + const transform = { scale: option.size3D } as ITransform; + this.editor.cmdManager.execute('update-transform-batch', { + objects: object3Ds as any, + transforms: transform, + }); + } + }); + this.editor.pc.render(); + } + + updateObjectRenderInfo(objects: AnnotateObject[] | AnnotateObject) { + if (!Array.isArray(objects)) objects = [objects]; + + objects.forEach((obj) => { + const bsObject = obj as any as IBSObject; + const frame = bsObject.frame; + // TODO + // if (frame) frame.needSave = true; + + const userData = this.editor.getObjectUserData(obj); + const classId = userData.classId || ''; + const classConfig = this.editor.getClassType(classId); + if (obj instanceof Box) { + // obj.editConfig.resize = !userData.isStandard && userData.resultType !== Const.Fixed; + obj.color.setStyle(classConfig ? classConfig.color : '#ffffff'); + } else if (obj instanceof Object2D) { + obj.color = classConfig ? classConfig.color : '#ffffff'; + } + + // obj.dashed = !!userData.invisibleFlag || bsObject.isHolder; + }); + this.editor.pc.render(); + } + + setDataByTrackId( + trackIds: string | string[], + option: { userData: IUserData; size3D?: THREE.Vector3 } = { userData: {} }, + frames?: IFrame[], + size3DFilter: (f: IFrame, e: AnnotateObject) => boolean = () => true, + update = false, + ) { + let objects = this.getObjects(trackIds, frames, size3DFilter); + this.editor.cmdManager.withGroup(() => { + if (option.userData) { + const options: IUpdateObjectUserDataOption = { + data: option.userData, + objects: objects, + }; + + objects.length > 0 && + this.editor.cmdManager.execute('update-object-user-data', options); + } + + if (option.size3D) { + const object3Ds = objects.filter((e) => e instanceof Box); + objects = objects.filter((e) => e instanceof Box); + const transform = { scale: option.size3D } as ITransform; + + object3Ds.length > 0 && + this.editor.cmdManager.execute('update-transform-batch', { + objects: object3Ds as any, + transforms: transform, + }); + } + }); + this.editor.pc.render(); + } + updateTransformByTrackId(object: Box, start: ITransform, end: ITransform) { + const { userData } = object; + const objects = this.getObjects( + userData.trackId, + undefined, + (f, e) => e instanceof Box && e.uuid !== object.uuid, + ) as Box[]; + if (objects.length <= 0) return; + const offsetPos = end.position?.clone(); + if (offsetPos && start.position) { + offsetPos.x -= start.position.x; + offsetPos.y -= start.position.y; + offsetPos.z -= start.position.z; + } + const offsetRot = end.rotation?.clone(); + if (offsetRot && start.rotation) { + offsetRot.x -= start.rotation.x; + offsetRot.y -= start.rotation.y; + offsetRot.z -= start.rotation.z; + } + function formatRot(r: number) { + const pi2 = Math.PI * 2; + if (r < 0) r += pi2; + else if (r > pi2) r -= pi2; + return r; + } + const transformList: ITransform[] = []; + objects.forEach((e) => { + const scale = object.scale.clone(); + const pos = e.position.clone(); + const rot = e.rotation.clone(); + offsetPos && pos.add(offsetPos); + if (offsetRot) { + rot.set( + formatRot(rot.x + offsetRot.x), + formatRot(rot.y + offsetRot.y), + formatRot(rot.z + offsetRot.z), + ); + } + transformList.push({ scale, position: pos, rotation: rot }); + }); + this.editor.cmdManager.withGroup(() => { + this.editor.cmdManager.execute('update-transform-batch', { + objects, + transforms: transformList, + }); + }); + } +} diff --git a/frontend/pc-tool/src/packages/pc-editor/config/event.ts b/frontend/pc-tool/src/packages/pc-editor/config/event.ts index 95d36a55..d7161e03 100644 --- a/frontend/pc-tool/src/packages/pc-editor/config/event.ts +++ b/frontend/pc-tool/src/packages/pc-editor/config/event.ts @@ -11,8 +11,8 @@ const Event = { UPDATE_CLASS_EDIT: 'update_class_edit', // CLEAR_MERGE_SPLIT: 'clear_merge_split', // PLAY_FRAME_CHANGE: 'play_frame_change', - // PLAY_STOP: 'play_stop', - // PLAY_START: 'play_start', + PLAY_STOP: 'play_stop', + PLAY_START: 'play_start', // PRE_MERGE_ACTION: 'pre_merge_action', // PRE_SPLIT_ACTION: 'pre_split_action', // UPDATE_TIME_LINE: 'update_time_line', @@ -25,6 +25,7 @@ const Event = { ANNOTATE_REMOVE: 'annotate_remove', ANNOTATE_LOAD: 'annotate_load', ANNOTATE_SELECT: 'annotate_select', + ANNOTATE_CLEAR: 'annotate_clear', // CURRENT_TRACK_CHANGE: 'current_track_change', // TRACK_OBJECT_CHANGE: 'track_object_change', @@ -36,6 +37,8 @@ const Event = { CHECK_UPDATE_VIEW: 'check_update_view', CHECK_UPDATE_INFO: 'check_update_info', + CURRENT_TRACK_CHANGE: 'current_track_change', + RESULT_EXPAND_TOGGLE: 'result_expand_toggle', RESIZE: 'resize', }; diff --git a/frontend/pc-tool/src/packages/pc-editor/config/hotkey.ts b/frontend/pc-tool/src/packages/pc-editor/config/hotkey.ts index 3717b9d5..745e1de0 100644 --- a/frontend/pc-tool/src/packages/pc-editor/config/hotkey.ts +++ b/frontend/pc-tool/src/packages/pc-editor/config/hotkey.ts @@ -44,6 +44,8 @@ const hotkeyConfig: IHotkeyConfig[] = [ // { key: 'right', action: 'nextFrame' }, { key: 'left', action: 'preFrame' }, + { key: `alt+right`, action: 'copyForward' }, + { key: `alt+left`, action: 'copyBackWard' }, ]; export default hotkeyConfig; diff --git a/frontend/pc-tool/src/packages/pc-editor/lang/en.ts b/frontend/pc-tool/src/packages/pc-editor/lang/en.ts index fc06cb97..016d67db 100644 --- a/frontend/pc-tool/src/packages/pc-editor/lang/en.ts +++ b/frontend/pc-tool/src/packages/pc-editor/lang/en.ts @@ -30,11 +30,33 @@ const en = { 'network-error': 'Network Error', 'login-invalid': 'Login Invalid', 'not-login': 'Not logged in', - 'retry':'retry', + retry: 'retry', // msg 'msg-not-save': "You didn't save changes?", 'create-rect-valid': "Don't beyond the picture", + + warnNoObject: 'Please select an object', + btnDelete: 'Delete', + deleteTitle: 'You are going to delete this tracking object from all frames.', + btnCancelText: 'Cancel', + successDelete: 'Successfully Deleted', + errorDelete: 'Delete has failed. Please try again', + selectObject: 'Select an Object', + noPlayData: 'No Play Data', + autoLoad: 'Auto-load', + speedDown: 'Speed Down({{n}} times)', + speedUp: 'Speed Up({{n}} times)', + replay: 'Replay', + pre: 'Pre(←)', + next: 'Next(→)', + play: 'Play({{n}} times)', + pause: 'Pause', + delete: 'Delete in All Frames', + timeLine: 'Timeline', + newBadge: 'New', + copyLeft1: 'Copy one frame backward (Alt+←)', + copyRight1: 'Copy one frame forward (Alt+→)', }; export type ILocale = typeof en; diff --git a/frontend/pc-tool/src/packages/pc-editor/lang/zh.ts b/frontend/pc-tool/src/packages/pc-editor/lang/zh.ts index 0133078d..7c1ea418 100644 --- a/frontend/pc-tool/src/packages/pc-editor/lang/zh.ts +++ b/frontend/pc-tool/src/packages/pc-editor/lang/zh.ts @@ -31,11 +31,32 @@ const zh: ILocale = { 'network-error': '网络错误', 'login-invalid': '登录过期', 'not-login': '未登录', - 'retry':'重试', + retry: '重试', // msg 'msg-not-save': '是否保存变更?', 'create-rect-valid': '不允许标注到图片外', + warnNoObject: '请选择一个对象', + btnDelete: '删除', + deleteTitle: '您将在所有帧中删除此追踪对象.', + btnCancelText: '取消', + successDelete: '删除成功', + errorDelete: '删除失败, 请重试', + selectObject: '选择一个对象', + noPlayData: '无播放数据', + autoLoad: '自动加载', + speedDown: '{{n}} 倍速', + speedUp: '{{n}} 倍速', + replay: '重新播放', + pre: '上一帧(←)', + next: '下一帧(→)', + play: '播放({{n}} 倍速)', + pause: '暂停', + delete: '删除所有', + timeLine: '时间轴', + newBadge: '新', + copyLeft1: '向左复制一帧(Alt+←)', + copyRight1: '向右复制一帧(Alt+→)', }; export { zh }; diff --git a/frontend/pc-tool/src/packages/pc-editor/state.ts b/frontend/pc-tool/src/packages/pc-editor/state.ts index ad7b103d..4cc2c9fe 100644 --- a/frontend/pc-tool/src/packages/pc-editor/state.ts +++ b/frontend/pc-tool/src/packages/pc-editor/state.ts @@ -35,7 +35,8 @@ export interface IState { filterActive: string[]; classifications: IClassification[]; - // isSeriesFrame: boolean; + isSeriesFrame: boolean; + currentClass: string; models: IModel[]; modelConfig: IModelConfig; recentClass: IClassType[]; @@ -64,7 +65,8 @@ export function getDefaultState(): IState { frameIndex: -1, classifications: [], - // isSeriesFrame: false, + isSeriesFrame: false, + currentClass: '', models: [], modelConfig: { confidence: [0.5, 1], @@ -154,5 +156,7 @@ function getDefaultConfig(): IConfig { maxViewWidth: '100%', limitRect2Image: true, withoutTaskId: withoutTaskId, + + autoLoad: false, }; } diff --git a/frontend/pc-tool/src/packages/pc-editor/type.ts b/frontend/pc-tool/src/packages/pc-editor/type.ts index 1b436aad..d4838e7a 100644 --- a/frontend/pc-tool/src/packages/pc-editor/type.ts +++ b/frontend/pc-tool/src/packages/pc-editor/type.ts @@ -17,7 +17,28 @@ export enum AttrType { DROPDOWN = 'DROPDOWN', TEXT = 'TEXT', } - +export interface ITrackCount { + object3D: number; + // 每个2d视图中相应的统计值,有多个2d视图 + object2D: IInfo2D[]; + count: number; +} +export interface IInfo2D { + [ObjectType.TYPE_2D_BOX]: number; + [ObjectType.TYPE_2D_RECT]: number; +} +export interface IBSObject { + isHolder: boolean; + needValid?: boolean; + frame: IFrame; + // timeStamp + lastTime: number; + updateTime: number; + createdAt?: string; + createdBy?: any; + classVersion?: number; + version?: number; +} export enum Const { Fixed = 'Fixed', Dynamic = 'Dynamic', @@ -121,6 +142,7 @@ export interface IUserData { createdAt?: string; sourceId?: string; sourceType?: string; + viewIndex?: number; } export interface IObject extends IUserData { @@ -235,6 +257,8 @@ export interface IConfig { maxViewHeight: string; maxViewWidth: string; limitRect2Image: boolean; + + autoLoad: boolean; } export interface IAnnotationInfo { diff --git a/frontend/pc-tool/src/packages/pc-editor/utils/data.ts b/frontend/pc-tool/src/packages/pc-editor/utils/data.ts index 104bd7da..f902c4bd 100644 --- a/frontend/pc-tool/src/packages/pc-editor/utils/data.ts +++ b/frontend/pc-tool/src/packages/pc-editor/utils/data.ts @@ -4,100 +4,100 @@ import { convertObject2Annotate } from './result'; import * as THREE from 'three'; import { setIdInfo } from './create'; -// export function copyData(editor: Editor, copyId: string, toIds: string[], objects: Box[]) { -// // let { dataManager, editor, state } = tool; -// // let { frames } = editor.state; - -// let updateDatas = { objects: [], data: [] } as { objects: AnnotateObject[]; data: IUserData[] }; -// let updateTrans = { objects: [], transforms: [] } as { -// objects: Box[]; -// transforms: ITransform[]; -// }; -// let addDatas = [] as { objects: AnnotateObject[]; frame: IFrame }[]; - -// // debugger; -// toIds.forEach((id) => { -// if (id === copyId) return; - -// let frame = editor.getFrame(id); -// let oldObjects = editor.dataManager.dataMap.get(id) || []; -// let oldMap = {} as Record; -// oldObjects.forEach((e: AnnotateObject) => { -// if (!(e instanceof Box)) return; -// oldMap[e.userData.trackId] = e; -// }); - -// let addOption = { objects: [], frame: frame } as { -// objects: AnnotateObject[]; -// frame: IFrame; -// }; - -// objects.forEach((annotate) => { -// let userData = annotate.userData as Required; - -// let trackId = userData.trackId; -// let object = oldMap[trackId]; - -// if (object) { -// let updateData = object.userData as IUserData; -// updateData.trackName = userData.trackName; -// // updateData.resultType = userData.resultType; -// // updateData.isStandard = userData.isStandard; -// updateData.classType = userData.classType; -// // updateData.resultStatus = Const.Copied; -// updateData.attrs = JSON.parse(JSON.stringify(userData.attrs || {})); - -// // object.position.copy(annotate.position); -// // TODO -// (object as any).frame = frame; - -// updateDatas.objects.push(object); -// updateDatas.data.push(updateData); - -// updateTrans.objects.push(object); -// updateTrans.transforms.push({ -// position: annotate.position, -// scale: annotate.scale, -// rotation: annotate.rotation, -// }); -// } else { -// let newUserData = JSON.parse(JSON.stringify(userData)) as IUserData; -// newUserData.backId = ''; -// newUserData.pointN = undefined; -// // newUserData.resultStatus = Const.Copied; - -// // newUserData.cBy = ''; - -// object = editor.createAnnotate3D( -// annotate.position, -// annotate.scale, -// annotate.rotation, -// newUserData, -// ); - -// addOption.objects.push(object); -// editor.updateObjectRenderInfo(object); -// } -// }); - -// if (addOption.objects.length > 0) addDatas.push(addOption); -// }); - -// editor.cmdManager.withGroup(() => { -// if (addDatas.length > 0) { -// editor.cmdManager.execute('add-object', addDatas); -// } - -// if (updateDatas.objects.length > 0) { -// // updateDatas -// editor.cmdManager.execute('update-object-user-data', updateDatas); -// } - -// if (updateTrans.objects.length > 0) { -// editor.cmdManager.execute('update-transform-batch', updateTrans); -// } -// }); -// } +export function copyData(editor: Editor, copyId: string, toIds: string[], objects: Box[]) { + // let { dataManager, editor, state } = tool; + // let { frames } = editor.state; + + let updateDatas = { objects: [], data: [] } as { objects: AnnotateObject[]; data: IUserData[] }; + let updateTrans = { objects: [], transforms: [] } as { + objects: Box[]; + transforms: ITransform[]; + }; + let addDatas = [] as { objects: AnnotateObject[]; frame: IFrame }[]; + + // debugger; + toIds.forEach((id) => { + if (id === copyId) return; + + let frame = editor.getFrame(id); + let oldObjects = editor.dataManager.dataMap.get(id) || []; + let oldMap = {} as Record; + oldObjects.forEach((e: AnnotateObject) => { + if (!(e instanceof Box)) return; + oldMap[e.userData.trackId] = e; + }); + + let addOption = { objects: [], frame: frame } as { + objects: AnnotateObject[]; + frame: IFrame; + }; + + objects.forEach((annotate) => { + let userData = annotate.userData as Required; + + let trackId = userData.trackId; + let object = oldMap[trackId]; + + if (object) { + let updateData = object.userData as IUserData; + updateData.trackName = userData.trackName; + // updateData.resultType = userData.resultType; + // updateData.isStandard = userData.isStandard; + updateData.classType = userData.classType; + // updateData.resultStatus = Const.Copied; + updateData.attrs = JSON.parse(JSON.stringify(userData.attrs || {})); + + // object.position.copy(annotate.position); + // TODO + (object as any).frame = frame; + + updateDatas.objects.push(object); + updateDatas.data.push(updateData); + + updateTrans.objects.push(object); + updateTrans.transforms.push({ + position: annotate.position, + scale: annotate.scale, + rotation: annotate.rotation, + }); + } else { + let newUserData = JSON.parse(JSON.stringify(userData)) as IUserData; + newUserData.backId = ''; + newUserData.pointN = undefined; + // newUserData.resultStatus = Const.Copied; + + // newUserData.cBy = ''; + + object = editor.createAnnotate3D( + annotate.position, + annotate.scale, + annotate.rotation, + newUserData, + ); + + addOption.objects.push(object); + editor.updateObjectRenderInfo(object); + } + }); + + if (addOption.objects.length > 0) addDatas.push(addOption); + }); + + editor.cmdManager.withGroup(() => { + if (addDatas.length > 0) { + editor.cmdManager.execute('add-object', addDatas); + } + + if (updateDatas.objects.length > 0) { + // updateDatas + editor.cmdManager.execute('update-object-user-data', updateDatas); + } + + if (updateTrans.objects.length > 0) { + editor.cmdManager.execute('update-transform-batch', updateTrans); + } + }); +} export function addModelTrackData(editor: Editor, objectsMap: Record) { let curFrame = editor.getCurrentFrame(); diff --git a/frontend/pc-tool/src/packages/pc-editor/utils/point.ts b/frontend/pc-tool/src/packages/pc-editor/utils/point.ts index 753ff791..45a273ea 100644 --- a/frontend/pc-tool/src/packages/pc-editor/utils/point.ts +++ b/frontend/pc-tool/src/packages/pc-editor/utils/point.ts @@ -114,39 +114,47 @@ export function filterPosition(positions: THREE.BufferAttribute, zRange: [number return filterPosition; } export function getMiniBox1( - transform: Required, + transform: Required>, positions: THREE.BufferAttribute, heightRange: [number, number], + autoFit = true, ) { - let matrix = new THREE.Matrix4(); + const matrix = new THREE.Matrix4(); const quaternion = new THREE.Quaternion().setFromEuler(transform.rotation); matrix.compose(transform.position, quaternion, transform.scale); let invertMatrix = new THREE.Matrix4().copy(matrix).invert(); - let pos = new THREE.Vector3(); - let box = new THREE.Box3(new THREE.Vector3(-0.5, -0.5, -0.5), new THREE.Vector3(0.5, 0.5, 0.5)); + const pos = new THREE.Vector3(); + const box = new THREE.Box3( + new THREE.Vector3(-0.5, -0.5, -0.5), + new THREE.Vector3(0.5, 0.5, 0.5), + ); // let newBox = new THREE.Box3(new THREE.Vector3(), new THREE.Vector3()); // let pointN = 0; - let offsetFloor = 0.15; // ground offset + const offsetFloor = 0.15; // 地面偏移 let preData: THREE.Vector3[] = []; setPreData(); if (preData.length === 0) return; - + const valid = function (infoRange: { infoMax: number; infoMin: number }) { + return infoRange.infoMax > infoRange.infoMin; + }; // filter z - let info = statisticPositionVInfo(preData); - let infoRange = getMaxMinInfo(info); - // console.log('info', info, infoRange); - // ground offset - if (infoRange.infoMin + offsetFloor < infoRange.infoMax) { - infoRange.infoMin += offsetFloor; + const zInfo = statisticPositionVInfo(preData); + const zInfoRange = getMaxMinInfo(zInfo); + // 地面偏移 + if (zInfoRange.infoMin + offsetFloor < zInfoRange.infoMax) { + zInfoRange.infoMin += offsetFloor; } - if (infoRange.infoMax <= infoRange.infoMin) return; - preData = preData.filter((e) => e.z >= infoRange.infoMin && e.z <= infoRange.infoMax); - transform.position.z = (infoRange.infoMin + infoRange.infoMax) / 2; - transform.scale.z = Math.abs(infoRange.infoMax - infoRange.infoMin); + if (!valid(zInfoRange)) return; + + preData = preData.filter((e) => e.z >= zInfoRange.infoMin && e.z <= zInfoRange.infoMax); + if (autoFit) { + transform.scale.z = Math.abs(zInfoRange.infoMax - zInfoRange.infoMin); + } + transform.position.z = (zInfoRange.infoMin + zInfoRange.infoMax) / 2; // update matrix matrix.compose(transform.position, quaternion, transform.scale); @@ -157,28 +165,30 @@ export function getMiniBox1( }); // x - info = statisticPositionVInfo(preData, 2, 'x'); - infoRange = getMaxMinInfo(info, { filter: 0 } as any); - // console.log('info', info, infoRange); - let positionX = (infoRange.infoMin + infoRange.infoMax) / 2; - transform.scale.x *= Math.abs(infoRange.infoMax - infoRange.infoMin); + const xInfo = statisticPositionVInfo(preData, 2, 'x'); + const xInfoRange = getMaxMinInfo(xInfo, { filter: 0 } as any); + const positionX = (xInfoRange.infoMin + xInfoRange.infoMax) / 2; // y - info = statisticPositionVInfo(preData, 2, 'y'); - infoRange = getMaxMinInfo(info, { filter: 0 } as any); - // console.log('info', info, infoRange); - let positionY = (infoRange.infoMin + infoRange.infoMax) / 2; - transform.scale.y *= Math.abs(infoRange.infoMax - infoRange.infoMin); + const yInfo = statisticPositionVInfo(preData, 2, 'y'); + const yInfoRange = getMaxMinInfo(yInfo, { filter: 0 } as any); + const positionY = (yInfoRange.infoMin + yInfoRange.infoMax) / 2; - let center = new THREE.Vector3(positionX, positionY, 0); + const center = new THREE.Vector3(positionX, positionY, 0); center.applyMatrix4(matrix); - transform.position.copy(center); + if (autoFit) { + transform.scale.x *= Math.abs(xInfoRange.infoMax - xInfoRange.infoMin); + transform.scale.y *= Math.abs(yInfoRange.infoMax - yInfoRange.infoMin); + transform.position.copy(center); + } else { + transform.position.z = center.z; + } function setPreData() { for (let i = 0; i < positions.count; i++) { - let x = positions.getX(i); - let y = positions.getY(i); - let z = positions.getZ(i); + const x = positions.getX(i); + const y = positions.getY(i); + const z = positions.getZ(i); pos.set(x, y, z).applyMatrix4(invertMatrix); if ( box.min.x <= pos.x && diff --git a/frontend/pc-tool/src/packages/pc-editor/utils/track.ts b/frontend/pc-tool/src/packages/pc-editor/utils/track.ts index 4c24c21b..a4b6654f 100644 --- a/frontend/pc-tool/src/packages/pc-editor/utils/track.ts +++ b/frontend/pc-tool/src/packages/pc-editor/utils/track.ts @@ -22,6 +22,7 @@ export function getTrackFromObject(info: Record) { }; } else { Object.assign(obj, globalTrack[trackId]); + obj.resultType = globalTrack[trackId].resultType; } }); }); diff --git a/frontend/pc-tool/src/packages/pc-render/PointCloud.ts b/frontend/pc-tool/src/packages/pc-render/PointCloud.ts index def73d41..ac2df36f 100644 --- a/frontend/pc-tool/src/packages/pc-render/PointCloud.ts +++ b/frontend/pc-tool/src/packages/pc-render/PointCloud.ts @@ -161,7 +161,10 @@ export default class PointCloud extends THREE.EventDispatcher { }); this.render(); } - + selectObjectById(id: string) { + const objects = [...this.getAnnotate3D(), ...this.getAnnotate2D()]; + this.selectObject(objects.find((o) => o.uuid == id)); + } addObject(objects: AnnotateObject | AnnotateObject[]) { if (!isArray(objects)) objects = [objects]; diff --git a/frontend/pc-tool/src/packages/pc-render/objects/Box.ts b/frontend/pc-tool/src/packages/pc-render/objects/Box.ts index ce5fb4b8..92744698 100644 --- a/frontend/pc-tool/src/packages/pc-render/objects/Box.ts +++ b/frontend/pc-tool/src/packages/pc-render/objects/Box.ts @@ -2,6 +2,7 @@ import * as THREE from 'three'; import { Intersect } from '../type'; import { get } from '../utils/tempVar'; import { AnnotateType } from '../type'; +import { ObjectType } from 'pc-editor'; let defaultMaterial = new THREE.LineBasicMaterial({ color: 0xffffff, @@ -42,6 +43,7 @@ export default class Box extends THREE.LineSegments { annotateType: AnnotateType; editConfig: IEditConfig = { resize: true }; color: THREE.Color; + objectType = ObjectType.TYPE_3D_BOX; constructor() { super(defaultGeometry, defaultMaterial); diff --git a/frontend/pc-tool/src/packages/pc-render/objects/object2d.ts b/frontend/pc-tool/src/packages/pc-render/objects/object2d.ts index 795d327d..69f7c09f 100644 --- a/frontend/pc-tool/src/packages/pc-render/objects/object2d.ts +++ b/frontend/pc-tool/src/packages/pc-render/objects/object2d.ts @@ -1,6 +1,7 @@ import * as THREE from 'three'; import { AnnotateType } from '../type'; import { isPointInRect } from '../utils'; +import { ObjectType } from 'pc-editor'; export class Object2D { connectId: number = -1; @@ -12,6 +13,7 @@ export class Object2D { annotateType: AnnotateType; position: any; color: string; + objectType = ObjectType.TYPE_2D_RECT; constructor() { this.userData = {}; this.annotateType = AnnotateType.ANNOTATE_2D; @@ -29,6 +31,7 @@ let temp2 = new THREE.Vector2(); export class Rect extends Object2D { center: THREE.Vector2 = new THREE.Vector2(); size: THREE.Vector2 = new THREE.Vector2(); + objectType = ObjectType.TYPE_2D_RECT; constructor(center?: THREE.Vector2, size?: THREE.Vector2) { super(); if (center) this.center.copy(center); @@ -49,6 +52,7 @@ let tempVector2Of4 = getVector2Of4(); export class Box2D extends Object2D { positions1: Vector2Of4 = getVector2Of4(); positions2: Vector2Of4 = getVector2Of4(); + objectType = ObjectType.TYPE_2D_BOX; constructor(positions1?: Vector2Of4, positions2?: Vector2Of4) { super(); diff --git a/frontend/pc-tool/src/pages/execute.ts b/frontend/pc-tool/src/pages/execute.ts index f3815fc8..2eb2d992 100644 --- a/frontend/pc-tool/src/pages/execute.ts +++ b/frontend/pc-tool/src/pages/execute.ts @@ -43,6 +43,10 @@ export function execute(): IPageHandler { // load model loadModels(), ]); + if (state.isSeriesFrame) { + await editor.loadManager.loadAllObjects(); + await editor.loadManager.loadAllClassification(); + } // load first data await editor.loadFrame(0, false); focusObject(); diff --git a/frontend/pc-tool/vite.config.ts b/frontend/pc-tool/vite.config.ts index 95a9ec9b..2b94cb4b 100644 --- a/frontend/pc-tool/vite.config.ts +++ b/frontend/pc-tool/vite.config.ts @@ -13,7 +13,8 @@ const config = defineConfig({ proxy: { '/api': { changeOrigin: true, - target: 'http://localhost:8190', + // target: 'http://localhost:8190', + target: 'https://xtreme1.alidev.beisai.com', }, }, },