@zzalai/leafer-multi-roi 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +293 -0
- package/README_EN.md +293 -0
- package/docs/assets/index-B2aZIWia.css +1 -0
- package/docs/assets/index-BrSsc-mD.js +1 -0
- package/docs/assets/vite-CMPW0ETM.svg +130 -0
- package/docs/index.html +14 -0
- package/index.html +13 -0
- package/package.json +61 -0
- package/project-docs/ARCHITECTURE.md +129 -0
- package/project-docs/REQUIREMENTS.md +113 -0
- package/src/App.vue +284 -0
- package/src/components/RoiEditor.vue +1544 -0
- package/src/index.ts +10 -0
- package/src/main.ts +4 -0
- package/src/types/index.ts +49 -0
- package/src/utils/coordinates.ts +46 -0
- package/src/utils/icons.ts +41 -0
- package/src/utils/uuid.ts +4 -0
- package/src/vite-env.d.ts +7 -0
- package/tsconfig.json +25 -0
- package/tsconfig.node.json +11 -0
- package/vite.config.ts +39 -0
- package/vite.docs.config.ts +29 -0
- package/vite.svg +130 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<svg width="48" height="46" viewBox="0 0 48 46" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M25.9456 44.9383C25.2821 45.7827 23.925 45.3131 23.925 44.2403V33.9369C23.925 32.6875 22.9126 31.6751 21.6631 31.6751H10.287C9.36714 31.6751 8.83075 30.6346 9.36713 29.8871L16.8464 19.4157C17.917 17.9185 16.8464 15.8376 15.0046 15.8376H1.23731C0.317479 15.8376 -0.218913 14.7972 0.317475 14.0497L10.0134 0.4741C10.2266 0.176825 10.5692 0.000183105 10.9332 0.000183105H39.8271C40.7469 0.000183105 41.2833 1.04065 40.7469 1.78814L33.2676 12.2595C32.197 13.7567 33.2676 15.8376 35.1094 15.8376H46.4856C47.4291 15.8376 47.959 16.9255 47.3753 17.6687L25.9478 44.9404L25.9456 44.9383Z" fill="#863BFF" style="fill:#863BFF;fill:color(display-p3 0.5252 0.2300 1.0000);fill-opacity:1;"/>
|
|
3
|
+
<mask id="mask0_2002_17158" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="48" height="46">
|
|
4
|
+
<path d="M25.8416 44.9381C25.1781 45.7825 23.821 45.3129 23.821 44.2401V33.9368C23.821 32.6873 22.8085 31.6749 21.5591 31.6749H10.183C9.26313 31.6749 8.72674 30.6344 9.26313 29.8869L16.7424 19.4155C17.813 17.9184 16.7424 15.8374 14.9006 15.8374H1.1333C0.213475 15.8374 -0.322917 14.797 0.213471 14.0495L9.90938 0.473917C10.1226 0.176641 10.4652 0 10.8292 0H39.7231C40.6429 0 41.1793 1.04046 40.6429 1.78796L33.1636 12.2594C32.093 13.7565 33.1636 15.8374 35.0054 15.8374H46.3816C47.3251 15.8374 47.855 16.9253 47.2713 17.6685L25.8438 44.9402L25.8416 44.9381Z" fill="black" style="fill:black;fill-opacity:1;"/>
|
|
5
|
+
</mask>
|
|
6
|
+
<g mask="url(#mask0_2002_17158)">
|
|
7
|
+
<g filter="url(#filter0_f_2002_17158)">
|
|
8
|
+
<ellipse cx="5.50802" cy="14.7043" rx="5.50802" ry="14.7043" transform="matrix(0.00324134 0.999995 0.999995 -0.00324134 -4.46924 31.5157)" fill="#EDE6FF" style="fill:#EDE6FF;fill:color(display-p3 0.9275 0.9033 1.0000);fill-opacity:1;"/>
|
|
9
|
+
</g>
|
|
10
|
+
<g filter="url(#filter1_f_2002_17158)">
|
|
11
|
+
<ellipse cx="10.3995" cy="29.8514" rx="10.3995" ry="29.8514" transform="matrix(0.00324134 0.999995 0.999995 -0.00324134 -39.3281 7.88272)" fill="#EDE6FF" style="fill:#EDE6FF;fill:color(display-p3 0.9275 0.9033 1.0000);fill-opacity:1;"/>
|
|
12
|
+
</g>
|
|
13
|
+
<g filter="url(#filter2_f_2002_17158)">
|
|
14
|
+
<ellipse cx="5.50802" cy="30.4868" rx="5.50802" ry="30.4868" transform="matrix(0.00324134 0.999995 0.999995 -0.00324134 -40.4673 11.3212)" fill="#7E14FF" style="fill:#7E14FF;fill:color(display-p3 0.4922 0.0767 1.0000);fill-opacity:1;"/>
|
|
15
|
+
</g>
|
|
16
|
+
<g filter="url(#filter3_f_2002_17158)">
|
|
17
|
+
<ellipse cx="5.50802" cy="30.5986" rx="5.50802" ry="30.5986" transform="matrix(0.00324134 0.999995 0.999995 -0.00324134 -35.8721 29.3204)" fill="#7E14FF" style="fill:#7E14FF;fill:color(display-p3 0.4922 0.0767 1.0000);fill-opacity:1;"/>
|
|
18
|
+
</g>
|
|
19
|
+
<g filter="url(#filter4_f_2002_17158)">
|
|
20
|
+
<ellipse cx="5.50802" cy="30.5986" rx="5.50802" ry="30.5986" transform="matrix(0.00324134 0.999995 0.999995 -0.00324134 -34.3398 30.4693)" fill="#7E14FF" style="fill:#7E14FF;fill:color(display-p3 0.4922 0.0767 1.0000);fill-opacity:1;"/>
|
|
21
|
+
</g>
|
|
22
|
+
<g filter="url(#filter5_f_2002_17158)">
|
|
23
|
+
<ellipse cx="14.0715" cy="22.0783" rx="14.0715" ry="22.0783" transform="matrix(0.0584509 -0.99829 -0.99829 -0.0584509 74.3486 26.8633)" fill="#EDE6FF" style="fill:#EDE6FF;fill:color(display-p3 0.9275 0.9033 1.0000);fill-opacity:1;"/>
|
|
24
|
+
</g>
|
|
25
|
+
<g filter="url(#filter6_f_2002_17158)">
|
|
26
|
+
<ellipse cx="3.47034" cy="21.5008" rx="3.47034" ry="21.5008" transform="matrix(-0.0172986 -0.99985 -0.99985 0.0172986 75.7944 18.0627)" fill="#7E14FF" style="fill:#7E14FF;fill:color(display-p3 0.4922 0.0767 1.0000);fill-opacity:1;"/>
|
|
27
|
+
</g>
|
|
28
|
+
<g filter="url(#filter7_f_2002_17158)">
|
|
29
|
+
<ellipse cx="3.47034" cy="21.5008" rx="3.47034" ry="21.5008" transform="matrix(-0.0172986 -0.99985 -0.99985 0.0172986 75.7944 18.0627)" fill="#7E14FF" style="fill:#7E14FF;fill:color(display-p3 0.4922 0.0767 1.0000);fill-opacity:1;"/>
|
|
30
|
+
</g>
|
|
31
|
+
<g filter="url(#filter8_f_2002_17158)">
|
|
32
|
+
<ellipse cx="0.386861" cy="8.97156" rx="4.40666" ry="29.1076" transform="rotate(39.5103 0.386861 8.97156)" fill="#7E14FF" style="fill:#7E14FF;fill:color(display-p3 0.4922 0.0767 1.0000);fill-opacity:1;"/>
|
|
33
|
+
</g>
|
|
34
|
+
<g filter="url(#filter9_f_2002_17158)">
|
|
35
|
+
<ellipse cx="47.5226" cy="-6.09166" rx="4.40666" ry="29.1076" transform="rotate(37.8923 47.5226 -6.09166)" fill="#7E14FF" style="fill:#7E14FF;fill:color(display-p3 0.4922 0.0767 1.0000);fill-opacity:1;"/>
|
|
36
|
+
</g>
|
|
37
|
+
<g filter="url(#filter10_f_2002_17158)">
|
|
38
|
+
<ellipse cx="41.4121" cy="6.3335" rx="5.9715" ry="9.66515" transform="rotate(37.8923 41.4121 6.3335)" fill="#47BFFF" style="fill:#47BFFF;fill:color(display-p3 0.2799 0.7480 1.0000);fill-opacity:1;"/>
|
|
39
|
+
</g>
|
|
40
|
+
<g filter="url(#filter11_f_2002_17158)">
|
|
41
|
+
<ellipse cx="-1.87921" cy="38.3321" rx="4.40666" ry="29.1076" transform="rotate(37.8923 -1.87921 38.3321)" fill="#7E14FF" style="fill:#7E14FF;fill:color(display-p3 0.4922 0.0767 1.0000);fill-opacity:1;"/>
|
|
42
|
+
</g>
|
|
43
|
+
<g filter="url(#filter12_f_2002_17158)">
|
|
44
|
+
<ellipse cx="-1.87921" cy="38.3321" rx="4.40666" ry="29.1076" transform="rotate(37.8923 -1.87921 38.3321)" fill="#7E14FF" style="fill:#7E14FF;fill:color(display-p3 0.4922 0.0767 1.0000);fill-opacity:1;"/>
|
|
45
|
+
</g>
|
|
46
|
+
<g filter="url(#filter13_f_2002_17158)">
|
|
47
|
+
<ellipse cx="35.6511" cy="29.9069" rx="4.40666" ry="29.1076" transform="rotate(37.8923 35.6511 29.9069)" fill="#7E14FF" style="fill:#7E14FF;fill:color(display-p3 0.4922 0.0767 1.0000);fill-opacity:1;"/>
|
|
48
|
+
</g>
|
|
49
|
+
<g filter="url(#filter14_f_2002_17158)">
|
|
50
|
+
<ellipse cx="38.4178" cy="32.4" rx="5.9715" ry="15.2974" transform="rotate(37.8923 38.4178 32.4)" fill="#47BFFF" style="fill:#47BFFF;fill:color(display-p3 0.2799 0.7480 1.0000);fill-opacity:1;"/>
|
|
51
|
+
</g>
|
|
52
|
+
</g>
|
|
53
|
+
<defs>
|
|
54
|
+
<filter id="filter0_f_2002_17158" x="-19.7697" y="16.1493" width="60.0452" height="41.6535" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
55
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
56
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
57
|
+
<feGaussianBlur stdDeviation="7.65926" result="effect1_foregroundBlur_2002_17158"/>
|
|
58
|
+
</filter>
|
|
59
|
+
<filter id="filter1_f_2002_17158" x="-54.613" y="-7.53303" width="90.3397" height="51.4368" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
60
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
61
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
62
|
+
<feGaussianBlur stdDeviation="7.65926" result="effect1_foregroundBlur_2002_17158"/>
|
|
63
|
+
</filter>
|
|
64
|
+
<filter id="filter2_f_2002_17158" x="-49.6403" y="2.03032" width="79.3554" height="29.4" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
65
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
66
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
67
|
+
<feGaussianBlur stdDeviation="4.59556" result="effect1_foregroundBlur_2002_17158"/>
|
|
68
|
+
</filter>
|
|
69
|
+
<filter id="filter3_f_2002_17158" x="-45.0451" y="20.0292" width="79.579" height="29.4" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
70
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
71
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
72
|
+
<feGaussianBlur stdDeviation="4.59556" result="effect1_foregroundBlur_2002_17158"/>
|
|
73
|
+
</filter>
|
|
74
|
+
<filter id="filter4_f_2002_17158" x="-43.5129" y="21.1781" width="79.579" height="29.4" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
75
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
76
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
77
|
+
<feGaussianBlur stdDeviation="4.59556" result="effect1_foregroundBlur_2002_17158"/>
|
|
78
|
+
</filter>
|
|
79
|
+
<filter id="filter5_f_2002_17158" x="15.7557" y="-17.9006" width="74.7493" height="58.852" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
80
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
81
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
82
|
+
<feGaussianBlur stdDeviation="7.65926" result="effect1_foregroundBlur_2002_17158"/>
|
|
83
|
+
</filter>
|
|
84
|
+
<filter id="filter6_f_2002_17158" x="23.5481" y="2.28368" width="61.3773" height="25.3622" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
85
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
86
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
87
|
+
<feGaussianBlur stdDeviation="4.59556" result="effect1_foregroundBlur_2002_17158"/>
|
|
88
|
+
</filter>
|
|
89
|
+
<filter id="filter7_f_2002_17158" x="23.5481" y="2.28368" width="61.3773" height="25.3622" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
90
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
91
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
92
|
+
<feGaussianBlur stdDeviation="4.59556" result="effect1_foregroundBlur_2002_17158"/>
|
|
93
|
+
</filter>
|
|
94
|
+
<filter id="filter8_f_2002_17158" x="-27.6359" y="-22.8531" width="56.0453" height="63.6493" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
95
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
96
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
97
|
+
<feGaussianBlur stdDeviation="4.59556" result="effect1_foregroundBlur_2002_17158"/>
|
|
98
|
+
</filter>
|
|
99
|
+
<filter id="filter9_f_2002_17158" x="20.1155" y="-38.4147" width="54.8139" height="64.646" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
100
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
101
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
102
|
+
<feGaussianBlur stdDeviation="4.59556" result="effect1_foregroundBlur_2002_17158"/>
|
|
103
|
+
</filter>
|
|
104
|
+
<filter id="filter10_f_2002_17158" x="24.6414" y="-11.3229" width="33.5414" height="35.3129" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
105
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
106
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
107
|
+
<feGaussianBlur stdDeviation="4.59556" result="effect1_foregroundBlur_2002_17158"/>
|
|
108
|
+
</filter>
|
|
109
|
+
<filter id="filter11_f_2002_17158" x="-29.2863" y="6.00905" width="54.8139" height="64.646" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
110
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
111
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
112
|
+
<feGaussianBlur stdDeviation="4.59556" result="effect1_foregroundBlur_2002_17158"/>
|
|
113
|
+
</filter>
|
|
114
|
+
<filter id="filter12_f_2002_17158" x="-29.2863" y="6.00905" width="54.8139" height="64.646" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
115
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
116
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
117
|
+
<feGaussianBlur stdDeviation="4.59556" result="effect1_foregroundBlur_2002_17158"/>
|
|
118
|
+
</filter>
|
|
119
|
+
<filter id="filter13_f_2002_17158" x="8.24395" y="-2.41615" width="54.8139" height="64.646" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
120
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
121
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
122
|
+
<feGaussianBlur stdDeviation="4.59556" result="effect1_foregroundBlur_2002_17158"/>
|
|
123
|
+
</filter>
|
|
124
|
+
<filter id="filter14_f_2002_17158" x="18.7132" y="10.5885" width="39.4091" height="43.6229" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
125
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
126
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
127
|
+
<feGaussianBlur stdDeviation="4.59556" result="effect1_foregroundBlur_2002_17158"/>
|
|
128
|
+
</filter>
|
|
129
|
+
</defs>
|
|
130
|
+
</svg>
|
package/docs/index.html
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="./assets/vite-CMPW0ETM.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>LeaferJS Multi ROI Test</title>
|
|
8
|
+
<script type="module" crossorigin src="./assets/index-BrSsc-mD.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="./assets/index-B2aZIWia.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="app"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
package/index.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>LeaferJS Multi ROI Test</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="/src/main.ts"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zzalai/leafer-multi-roi",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Vue3 component for multi-region selection on images using LeaferJS",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/leafer-multi-roi.umd.js",
|
|
10
|
+
"module": "./dist/leafer-multi-roi.es.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/leafer-multi-roi.es.js",
|
|
15
|
+
"require": "./dist/leafer-multi-roi.umd.js",
|
|
16
|
+
"types": "./dist/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "vite",
|
|
21
|
+
"build": "vite build",
|
|
22
|
+
"preview": "vite preview",
|
|
23
|
+
"type-check": "tsc --noEmit",
|
|
24
|
+
"docs:build": "vite build --config vite.docs.config.ts"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"vue3",
|
|
28
|
+
"leaferjs",
|
|
29
|
+
"roi",
|
|
30
|
+
"annotation",
|
|
31
|
+
"image-processing"
|
|
32
|
+
],
|
|
33
|
+
"author": "zzalai",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/otaku1951/leafer-multi-roi.git"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://otaku1951.github.io/leafer-multi-roi/",
|
|
40
|
+
"packageManager": "pnpm@10.33.0",
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"vue": "^3.3.0"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@leafer-in/editor": "^2.0.8",
|
|
46
|
+
"@leafer-in/resize": "^2.0.8",
|
|
47
|
+
"@leafer-in/view": "^2.0.8",
|
|
48
|
+
"@leafer-in/viewport": "^2.0.8",
|
|
49
|
+
"@zzalai/leafer-undo-redo": "1.0.3",
|
|
50
|
+
"leafer-ui": "^2.0.8",
|
|
51
|
+
"tinykeys": "^3.0.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "^25.6.0",
|
|
55
|
+
"@vitejs/plugin-vue": "^6.0.6",
|
|
56
|
+
"terser": "^5.46.1",
|
|
57
|
+
"typescript": "^6.0.2",
|
|
58
|
+
"vite": "^8.0.8",
|
|
59
|
+
"vite-plugin-dts": "^3.0.0"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# 架构设计文档 (Architecture Design Document)
|
|
2
|
+
|
|
3
|
+
## 1. 总体架构目标
|
|
4
|
+
|
|
5
|
+
本项目的核心目标是构建一个高性能、可复用、可发布到 npm 的 Vue3 包插件,用于多区域框选(Multi-ROI Selection)。该工具将封装图片处理、ROI 交互、撤销/重做以及数据输出等核心逻辑,并通过提供统一的接口,使其能轻松集成到 Vue3 前端项目中。
|
|
6
|
+
|
|
7
|
+
## 2. 核心技术栈
|
|
8
|
+
|
|
9
|
+
本项目将尽可能利用 LeaferJS 官方提供的插件来满足需求。
|
|
10
|
+
|
|
11
|
+
* **画布渲染与交互**: [LeaferJS](https://www.leaferjs.com/)
|
|
12
|
+
* 包名: `leafer-ui` (pnpm)
|
|
13
|
+
* 插件:
|
|
14
|
+
* `@leafer-in/editor`: 用于 ROI 选区的编辑态管理。
|
|
15
|
+
* `@leafer-in/resize`: 配合 `editor` 插件实现 ROI 选区的大小变更。
|
|
16
|
+
* `@leafer-in/viewport`: 用于实现画布的平移和缩放功能。
|
|
17
|
+
* `@leafer-in/view`: 提供视图支持。
|
|
18
|
+
* 选择理由: LeaferJS 提供高效的 2D 渲染能力和丰富的图形对象模型,非常适合处理复杂的画布交互,如 ROI 的绘制、移动、缩放等。
|
|
19
|
+
* **语言**: [TypeScript](https://www.typescriptlang.org/)
|
|
20
|
+
* 选择理由: 提升代码的可维护性、可读性,减少运行时错误,并为大型项目提供更好的开发体验和类型安全。
|
|
21
|
+
* **撤销/重做**: [@zzalai/leafer-undo-redo](https://www.npmjs.com/package/@zzalai/leafer-undo-redo)(https://github.com/otaku1951/@zzalai/leafer-undo-redo)
|
|
22
|
+
* 选择理由: 专门为 LeaferJS 设计的撤销/重做库,可以无缝集成,简化状态管理。本项目将使用此插件来管理所有可撤销/重做操作,包括 ROI 的创建、删除、移动和大小调整。
|
|
23
|
+
* **热键管理**: [Tinykeys](https://github.com/jamiebuilds/tinykeys)
|
|
24
|
+
* 选择理由: 轻量级的键盘快捷键库,用于实现热键系统,支持复杂的键盘组合。
|
|
25
|
+
* **图标**: 直接嵌入 SVG 代码
|
|
26
|
+
* 选择理由: 减少依赖,提高性能,直接使用需要的 SVG 图标。
|
|
27
|
+
|
|
28
|
+
## 3. 模块结构设计
|
|
29
|
+
|
|
30
|
+
本项目采用单组件架构,将核心逻辑直接集成在 Vue 组件中,简化了架构设计并提高了开发效率。
|
|
31
|
+
|
|
32
|
+
### 3.0 用户交互模式设计
|
|
33
|
+
|
|
34
|
+
为了明确区分用户的操作意图并避免事件冲突,工具将引入不同的交互模式,并通过工具栏按钮进行切换。
|
|
35
|
+
|
|
36
|
+
* **“选中”模式 (Selection Mode)**: 对应鼠标箭头图标。
|
|
37
|
+
* 功能: 选择、移动、调整现有 ROI,以及取消选中。
|
|
38
|
+
* **“绘制”模式 (Draw Mode)**: 对应矩形绘制图标。
|
|
39
|
+
* 功能: 在图片上拖拽绘制新的矩形 ROI。
|
|
40
|
+
|
|
41
|
+
### 3.1 RoiEditor 组件
|
|
42
|
+
|
|
43
|
+
* **职责**: 封装所有与 ROI 工具相关的核心业务逻辑和 UI 界面。
|
|
44
|
+
* **内容**:
|
|
45
|
+
* **模板结构**:
|
|
46
|
+
* 画布容器 (`canvas-container`)
|
|
47
|
+
* 加载占位和错误状态
|
|
48
|
+
* 缩放控制器 (`zoom-controller`)
|
|
49
|
+
* 工具栏 (`toolbar`)
|
|
50
|
+
* **核心逻辑**:
|
|
51
|
+
* LeaferJS 画布实例的管理与初始化。
|
|
52
|
+
* ROI 对象的创建、存储、更新和删除。
|
|
53
|
+
* 与 `@zzalai/leafer-undo-redo` 的集成,管理所有可撤销/重做操作。
|
|
54
|
+
* 鼠标事件处理(绘制、选择、移动、缩放)。
|
|
55
|
+
* 热键事件处理(工具切换、撤销/重做、删除、缩放)。
|
|
56
|
+
* 图片加载(URL/Base64)及显示,包括:
|
|
57
|
+
* 加载时的过渡状态显示
|
|
58
|
+
* 加载失败的处理逻辑
|
|
59
|
+
* 坐标转换逻辑(屏幕坐标 <=> 原始图片坐标 <=> 归一化坐标)。
|
|
60
|
+
* ROI 数据输出(原始坐标、归一化坐标)。
|
|
61
|
+
* 画布整体 JSON 数据的导入/导出逻辑(用于还原或保存使用)。
|
|
62
|
+
* 样式配置的管理。
|
|
63
|
+
* **Props 定义**:
|
|
64
|
+
* `imageSource`: 图片源对象,包含 `id` (可选) 和 `url` (必填) 字段。
|
|
65
|
+
* `options`: 配置选项,包含 `regionStyle`、`selectedRegionStyle`、`maxRegions` 和 `maxUndoSteps` 字段。
|
|
66
|
+
* **Emits 事件**:
|
|
67
|
+
* `roiChange`: ROI 数据变化时触发。
|
|
68
|
+
* `loadStart`: 图片加载开始时触发。
|
|
69
|
+
* `loadSuccess`: 图片加载成功时触发。
|
|
70
|
+
* `loadError`: 图片加载失败时触发。
|
|
71
|
+
* `undoStateChange`: 撤销状态变化时触发。
|
|
72
|
+
* `redoStateChange`: 重做状态变化时触发。
|
|
73
|
+
* **暴露接口**:
|
|
74
|
+
* `getROIAnnotations`: 获取 ROI 标注数据。
|
|
75
|
+
* `getImageInfo`: 获取图片信息。
|
|
76
|
+
* `exportCanvasJSON`: 导出画布 JSON 数据。
|
|
77
|
+
* `importCanvasJSON`: 导入画布 JSON 数据。
|
|
78
|
+
* `loadImage`: 手动加载图片。
|
|
79
|
+
|
|
80
|
+
## 4. 样式管理
|
|
81
|
+
|
|
82
|
+
* **CSS 变量**: 使用 CSS 变量统一管理样式,前缀为 `leafer-roi-`,包括:
|
|
83
|
+
* 颜色变量(主色、背景色、文本色等)
|
|
84
|
+
* 尺寸变量(内边距、图标大小、按钮尺寸等)
|
|
85
|
+
* 边框圆角
|
|
86
|
+
* 阴影效果
|
|
87
|
+
* 动画时间
|
|
88
|
+
* **作用域样式**: 使用 Vue 的 `scoped` 样式确保样式隔离,避免影响全局样式。
|
|
89
|
+
|
|
90
|
+
## 5. 构建与发布
|
|
91
|
+
|
|
92
|
+
### 5.1 构建工具
|
|
93
|
+
|
|
94
|
+
* **指定**: [Vite](https://vitejs.dev/)
|
|
95
|
+
* 选择理由: 现代化的打包工具,能够高效地处理 TypeScript 和生成多种模块格式,非常适合库的开发。
|
|
96
|
+
|
|
97
|
+
### 5.2 构建配置
|
|
98
|
+
|
|
99
|
+
* **代码压缩**: 使用 Terser 进行代码压缩,移除 `console.log` 等调试信息。
|
|
100
|
+
* **配置示例**:
|
|
101
|
+
```typescript
|
|
102
|
+
build: {
|
|
103
|
+
minify: 'terser',
|
|
104
|
+
terserOptions: {
|
|
105
|
+
compress: {
|
|
106
|
+
drop_console: true,
|
|
107
|
+
drop_debugger: true,
|
|
108
|
+
pure_funcs: ['console.log']
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 5.3 npm 包结构
|
|
115
|
+
|
|
116
|
+
* `package.json`: 需包含 `main` (CJS), `module` (ESM), `types` (TypeScript 类型定义), `exports` (现代 Node.js 和打包工具的入口点) 等字段,以及 Vue3 插件所需的配置。
|
|
117
|
+
* **依赖管理**:
|
|
118
|
+
* `leafer-ui` 及其插件应作为 `dependencies`。
|
|
119
|
+
* `@zzalai/leafer-undo-redo` 应作为 `dependencies`。
|
|
120
|
+
* `tinykeys` 应作为 `dependencies`。
|
|
121
|
+
* `vue` 应作为 `peerDependency`,确保与宿主应用的 Vue3 版本兼容。
|
|
122
|
+
|
|
123
|
+
## 6. 开发与测试工作流
|
|
124
|
+
|
|
125
|
+
* **项目初始化**: 使用 Vite 搭建 TypeScript 项目,配置 Vue3 插件开发环境。
|
|
126
|
+
* **组件开发**: 直接在 `RoiEditor.vue` 中实现核心逻辑和 UI。
|
|
127
|
+
* **集成测试**: 开发完成后,使用 Vue3 作为宿主应用进行集成测试,验证组件的功能和与框架的集成效果。
|
|
128
|
+
* **文档编写**: 维护详细的 API 文档和使用示例,包括 Vue3 插件的使用方法。
|
|
129
|
+
* **测试**: 编写单元测试和集成测试以确保功能正确性和稳定性。
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# 多区域框选工具需求 (Multi-ROI Selection Tool Requirements)
|
|
2
|
+
|
|
3
|
+
## 1. 工具目标
|
|
4
|
+
|
|
5
|
+
本工具旨在提供一个交互式界面,允许用户在图片上进行多个矩形区域(Region of Interest, ROI)的框选和管理。最终,工具将输出原始图片信息以及所有框选区域的坐标数据(包括原始像素坐标和归一化坐标),用于人工智能 (AI) 进行图片特征提取。
|
|
6
|
+
|
|
7
|
+
## 2. 核心功能
|
|
8
|
+
|
|
9
|
+
### 2.1 图片加载
|
|
10
|
+
|
|
11
|
+
* **加载方式**: 工具初始化时,支持通过传入图片对象(包含id和url字段)进行加载,其中url可以是 HTTPS 绝对地址的图片 URL 或 Base64 编码的图片数据。
|
|
12
|
+
* **手动加载**: 提供 `loadImage` 方法,允许手动触发图片加载。
|
|
13
|
+
|
|
14
|
+
### 2.2 区域绘制与管理
|
|
15
|
+
|
|
16
|
+
* **区域类型**: 只支持绘制矩形区域,不允许旋转。
|
|
17
|
+
* **区域标识**: 每个区域都将生成一个唯一的 ID (使用 JavaScript 原生 UUID 方法)。
|
|
18
|
+
* **创建区域**: 用户可以通过鼠标在图片上拖拽绘制新区域。
|
|
19
|
+
* **移动区域**: 用户可以拖动已存在的区域来改变其位置。
|
|
20
|
+
* **边界限制**: 在移动过程中,区域的任何部分都不能超出原始图片范围。
|
|
21
|
+
* **调整区域大小**: 用户可以通过拖拽区域边缘或角点来改变区域的宽度和高度。
|
|
22
|
+
* **边界限制**: 在调整大小过程中,区域的任何部分都不能超出原始图片范围。
|
|
23
|
+
* **删除区域**:
|
|
24
|
+
* **选择**: 单击选中区域,或按住 `Shift` 键进行多选。
|
|
25
|
+
* **操作**: 选中区域后,可以通过键盘 `Delete` 键或删除按钮进行删除。
|
|
26
|
+
|
|
27
|
+
### 2.3 交互与用户体验
|
|
28
|
+
|
|
29
|
+
* **图片缩放**: 支持对加载的图片进行缩放操作(通过 UI 控件或热键)。
|
|
30
|
+
* **撤销 (Undo) / 重做 (Redo)**:
|
|
31
|
+
* **覆盖操作**: 支持对以下操作进行撤销和重做:新建区域、删除区域、移动区域、调整区域大小。
|
|
32
|
+
* **快捷键**: `Ctrl+Z` (撤销), `Ctrl+Y` (重做)。
|
|
33
|
+
* **热键系统**:
|
|
34
|
+
* **选择工具**: `V`
|
|
35
|
+
* **框选工具**: `M`
|
|
36
|
+
* **撤销**: `Ctrl+Z`
|
|
37
|
+
* **重做**: `Ctrl+Y`
|
|
38
|
+
* **删除**: `Delete`
|
|
39
|
+
* **放大**: `Ctrl++`
|
|
40
|
+
* **缩小**: `Ctrl+-`
|
|
41
|
+
* **重置缩放**: `Ctrl+0`
|
|
42
|
+
* **显示/隐藏热键提示**: `Alt`
|
|
43
|
+
* **生效条件**: 热键在画布区域获得焦点或鼠标在画布区域内时生效。
|
|
44
|
+
* **热键提示**: 按 `Alt` 键显示/隐藏热键提示,工具按钮和缩放控制器上会显示对应的热键。
|
|
45
|
+
* **区域样式**:
|
|
46
|
+
* **默认样式**: 区域需要有边框和背景色,使用同色系不同透明度。默认颜色为天蓝色。该颜色可在工具初始化时通过 `options.regionStyle` 配置。
|
|
47
|
+
* **选中样式**: 区域被选中后,显示高亮边框和背景色。高亮颜色可在工具初始化时通过 `options.selectedRegionStyle` 配置。
|
|
48
|
+
* **最大区域数量**: 工具默认限制最多创建 20 个区域,可通过 `options.maxRegions` 配置自定义限制。
|
|
49
|
+
* **撤销/重做步数限制**: 工具默认限制最多保留 100 步撤销/重做历史,可通过 `options.maxUndoSteps` 配置自定义限制。
|
|
50
|
+
|
|
51
|
+
## 3. 数据输出
|
|
52
|
+
|
|
53
|
+
### 3.1 坐标输出
|
|
54
|
+
|
|
55
|
+
* **基准**: 所有输出的坐标都必须基于图片 100% 原始大小(世界坐标/图片坐标)。
|
|
56
|
+
* **格式**: 每个区域输出为包含 ID、位置、大小和四个角点坐标的对象,多个区域形成一个数组。
|
|
57
|
+
* **示例**:
|
|
58
|
+
```json
|
|
59
|
+
[
|
|
60
|
+
{
|
|
61
|
+
"id": "unique-roi-id-1",
|
|
62
|
+
"x": 100,
|
|
63
|
+
"y": 50,
|
|
64
|
+
"width": 200,
|
|
65
|
+
"height": 100,
|
|
66
|
+
"points": [[100, 50], [300, 50], [300, 150], [100, 150]],
|
|
67
|
+
"normalized": {
|
|
68
|
+
"x": 0.1,
|
|
69
|
+
"y": 0.05,
|
|
70
|
+
"width": 0.2,
|
|
71
|
+
"height": 0.1,
|
|
72
|
+
"points": [[0.1, 0.05], [0.3, 0.05], [0.3, 0.15], [0.1, 0.15]]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
```
|
|
77
|
+
* **额外输出**: 除了原始像素坐标,还将额外输出对应的归一化数据(相对于图片宽度和高度的比例值)。
|
|
78
|
+
|
|
79
|
+
### 3.2 JSON 序列化
|
|
80
|
+
|
|
81
|
+
* **导出**: 能够将画布完整状态(包括图片信息、ROI 状态、画布配置)序列化为 JSON 字符串。
|
|
82
|
+
* **JSON 结构**:
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"version": "1.0",
|
|
86
|
+
"canvas": {
|
|
87
|
+
"width": 1000,
|
|
88
|
+
"height": 800,
|
|
89
|
+
"zoom": 1
|
|
90
|
+
},
|
|
91
|
+
"image": {
|
|
92
|
+
"id": "image-1",
|
|
93
|
+
"url": "https://example.com/image.jpg",
|
|
94
|
+
"width": 1000,
|
|
95
|
+
"height": 800
|
|
96
|
+
},
|
|
97
|
+
"leaferJSON": {...}, // LeaferJS 原生 JSON 数据
|
|
98
|
+
"annotations": [...], // ROI 标注数据
|
|
99
|
+
"exportedAt": "2024-01-01T00:00:00.000Z"
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
* **导入**: 能够从 JSON 字符串还原画布完整状态,包括图片加载和 ROI 重建。
|
|
103
|
+
|
|
104
|
+
### 3.3 图片信息获取
|
|
105
|
+
|
|
106
|
+
* **方法**: 提供 `getImageInfo` 方法,用于获取当前加载图片的信息(id、url、宽度、高度)。
|
|
107
|
+
|
|
108
|
+
## 4. 技术栈 (已确认)
|
|
109
|
+
|
|
110
|
+
* **核心库**: Leafer.js (用于图形渲染和交互)
|
|
111
|
+
* **撤销/重做**: @zzalai/leafer-undo-redo
|
|
112
|
+
* **热键管理**: Tinykeys
|
|
113
|
+
* **构建工具**: Vite
|