Spaces:
Running
Running
edge move
Browse files
comic_panel_extractor/static/annotator.html
CHANGED
|
@@ -649,6 +649,12 @@
|
|
| 649 |
this.lastMouseX = 0;
|
| 650 |
this.lastMouseY = 0;
|
| 651 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 652 |
this.init();
|
| 653 |
}
|
| 654 |
|
|
@@ -893,7 +899,7 @@
|
|
| 893 |
}
|
| 894 |
this.ctx.stroke();
|
| 895 |
|
| 896 |
-
// Draw points (larger for selected polygon)
|
| 897 |
const pointRadius = isSelected ? 6 : 4;
|
| 898 |
points.forEach((point, index) => {
|
| 899 |
this.ctx.fillStyle = isSelected ? '#0066ff' : strokeColor;
|
|
@@ -901,7 +907,6 @@
|
|
| 901 |
this.ctx.arc(point.x, point.y, pointRadius, 0, 2 * Math.PI);
|
| 902 |
this.ctx.fill();
|
| 903 |
|
| 904 |
-
// Draw point outline for selected polygon
|
| 905 |
if (isSelected) {
|
| 906 |
this.ctx.strokeStyle = '#ffffff';
|
| 907 |
this.ctx.lineWidth = 2;
|
|
@@ -909,19 +914,90 @@
|
|
| 909 |
}
|
| 910 |
});
|
| 911 |
|
| 912 |
-
// Draw edge
|
| 913 |
-
if (isSelected && points.length > 2) {
|
| 914 |
-
this.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 915 |
for (let i = 0; i < points.length; i++) {
|
| 916 |
const nextIndex = (i + 1) % points.length;
|
| 917 |
const midX = (points[i].x + points[nextIndex].x) / 2;
|
| 918 |
const midY = (points[i].y + points[nextIndex].y) / 2;
|
| 919 |
|
| 920 |
-
|
| 921 |
-
|
| 922 |
-
|
|
|
|
| 923 |
}
|
| 924 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 925 |
}
|
| 926 |
|
| 927 |
// Polygon-specific editing methods
|
|
@@ -1071,21 +1147,30 @@
|
|
| 1071 |
const pos = this.getMousePos(e);
|
| 1072 |
this.lastMouseX = pos.x;
|
| 1073 |
this.lastMouseY = pos.y;
|
|
|
|
| 1074 |
|
| 1075 |
if (this.annotationMode === 'segmentation') {
|
| 1076 |
-
// Check if clicking on
|
| 1077 |
if (this.selectedBoxIndex >= 0) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1078 |
const pointIndex = this.getPolygonPointAtPosition(pos.x, pos.y);
|
| 1079 |
if (pointIndex >= 0) {
|
| 1080 |
this.isDraggingPoint = true;
|
| 1081 |
this.draggingPointIndex = pointIndex;
|
| 1082 |
-
this.canvas.style.cursor =
|
| 1083 |
return;
|
| 1084 |
}
|
| 1085 |
|
| 1086 |
// Check if clicking on an edge to add a point
|
| 1087 |
const edgeIndex = this.getPolygonEdgeAtPosition(pos.x, pos.y);
|
| 1088 |
-
if (edgeIndex >= 0 && e.ctrlKey) {
|
| 1089 |
this.addPolygonPoint(this.selectedBoxIndex, edgeIndex, pos.x, pos.y);
|
| 1090 |
this.drawCanvas();
|
| 1091 |
return;
|
|
@@ -1099,7 +1184,7 @@
|
|
| 1099 |
this.isDragging = true;
|
| 1100 |
this.dragStartX = pos.x;
|
| 1101 |
this.dragStartY = pos.y;
|
| 1102 |
-
this.canvas.style.cursor =
|
| 1103 |
this.updateSelectedBoxInfo();
|
| 1104 |
this.drawCanvas();
|
| 1105 |
return;
|
|
@@ -1126,7 +1211,6 @@
|
|
| 1126 |
|
| 1127 |
// Original bbox logic remains the same...
|
| 1128 |
if (this.selectedBoxIndex >= 0) {
|
| 1129 |
-
const handle = this.getResizeHandle(pos.x, pos.y);
|
| 1130 |
if (handle) {
|
| 1131 |
this.isResizing = true;
|
| 1132 |
this.resizeHandle = handle;
|
|
@@ -1141,7 +1225,7 @@
|
|
| 1141 |
this.isDragging = true;
|
| 1142 |
this.dragStartX = pos.x;
|
| 1143 |
this.dragStartY = pos.y;
|
| 1144 |
-
this.canvas.style.cursor =
|
| 1145 |
this.updateSelectedBoxInfo();
|
| 1146 |
this.drawCanvas();
|
| 1147 |
return;
|
|
@@ -1189,6 +1273,13 @@
|
|
| 1189 |
const pos = this.getMousePos(e);
|
| 1190 |
|
| 1191 |
if (this.annotationMode === 'segmentation') {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1192 |
if (this.isDraggingPoint && this.selectedBoxIndex >= 0) {
|
| 1193 |
// Move individual point
|
| 1194 |
this.movePolygonPoint(this.selectedBoxIndex, this.draggingPointIndex, pos.x, pos.y);
|
|
@@ -1243,6 +1334,8 @@
|
|
| 1243 |
|
| 1244 |
onMouseUp() {
|
| 1245 |
if (this.annotationMode === 'segmentation') {
|
|
|
|
|
|
|
| 1246 |
this.isDraggingPoint = false;
|
| 1247 |
this.draggingPointIndex = -1;
|
| 1248 |
this.isDragging = false;
|
|
@@ -1292,9 +1385,17 @@
|
|
| 1292 |
}
|
| 1293 |
updatePolygonCursor(x, y) {
|
| 1294 |
if (this.selectedBoxIndex >= 0 && this.annotations[this.selectedBoxIndex].type === 'segmentation') {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1295 |
const pointIndex = this.getPolygonPointAtPosition(x, y);
|
| 1296 |
if (pointIndex >= 0) {
|
| 1297 |
-
this.canvas.style.cursor =
|
| 1298 |
return;
|
| 1299 |
}
|
| 1300 |
|
|
@@ -1305,7 +1406,7 @@
|
|
| 1305 |
}
|
| 1306 |
|
| 1307 |
if (this.isPointInPolygon(x, y, this.annotations[this.selectedBoxIndex].points)) {
|
| 1308 |
-
this.canvas.style.cursor =
|
| 1309 |
return;
|
| 1310 |
}
|
| 1311 |
}
|
|
@@ -1336,7 +1437,7 @@
|
|
| 1336 |
}
|
| 1337 |
|
| 1338 |
getResizeHandle(x, y) {
|
| 1339 |
-
if (this.selectedBoxIndex < 0) return
|
| 1340 |
|
| 1341 |
const box = this.annotations[this.selectedBoxIndex];
|
| 1342 |
const handleSize = 8;
|
|
@@ -1358,7 +1459,7 @@
|
|
| 1358 |
return handle;
|
| 1359 |
}
|
| 1360 |
}
|
| 1361 |
-
return
|
| 1362 |
}
|
| 1363 |
|
| 1364 |
getBoxAtPosition(x, y) {
|
|
|
|
| 649 |
this.lastMouseX = 0;
|
| 650 |
this.lastMouseY = 0;
|
| 651 |
|
| 652 |
+
// Add these new properties for edge resizing
|
| 653 |
+
this.isDraggingEdge = false;
|
| 654 |
+
this.draggingEdgeIndex = -1;
|
| 655 |
+
this.edgeResizeTolerance = 8;
|
| 656 |
+
this.showEdgeHandles = true;
|
| 657 |
+
|
| 658 |
this.init();
|
| 659 |
}
|
| 660 |
|
|
|
|
| 899 |
}
|
| 900 |
this.ctx.stroke();
|
| 901 |
|
| 902 |
+
// Draw corner points (larger for selected polygon)
|
| 903 |
const pointRadius = isSelected ? 6 : 4;
|
| 904 |
points.forEach((point, index) => {
|
| 905 |
this.ctx.fillStyle = isSelected ? '#0066ff' : strokeColor;
|
|
|
|
| 907 |
this.ctx.arc(point.x, point.y, pointRadius, 0, 2 * Math.PI);
|
| 908 |
this.ctx.fill();
|
| 909 |
|
|
|
|
| 910 |
if (isSelected) {
|
| 911 |
this.ctx.strokeStyle = '#ffffff';
|
| 912 |
this.ctx.lineWidth = 2;
|
|
|
|
| 914 |
}
|
| 915 |
});
|
| 916 |
|
| 917 |
+
// Draw edge handles for selected polygon
|
| 918 |
+
if (isSelected && points.length > 2 && this.showEdgeHandles) {
|
| 919 |
+
this.drawEdgeHandles(points);
|
| 920 |
+
}
|
| 921 |
+
}
|
| 922 |
+
drawEdgeHandles(points) {
|
| 923 |
+
this.ctx.fillStyle = 'rgba(0, 102, 255, 0.8)';
|
| 924 |
+
this.ctx.strokeStyle = '#ffffff';
|
| 925 |
+
this.ctx.lineWidth = 2;
|
| 926 |
+
|
| 927 |
+
for (let i = 0; i < points.length; i++) {
|
| 928 |
+
const nextIndex = (i + 1) % points.length;
|
| 929 |
+
const midX = (points[i].x + points[nextIndex].x) / 2;
|
| 930 |
+
const midY = (points[i].y + points[nextIndex].y) / 2;
|
| 931 |
+
|
| 932 |
+
// Draw square handle
|
| 933 |
+
const handleSize = 8;
|
| 934 |
+
this.ctx.fillRect(midX - handleSize / 2, midY - handleSize / 2, handleSize, handleSize);
|
| 935 |
+
this.ctx.strokeRect(midX - handleSize / 2, midY - handleSize / 2, handleSize, handleSize);
|
| 936 |
+
}
|
| 937 |
+
}
|
| 938 |
+
|
| 939 |
+
// NEW METHOD: Get edge handle at position
|
| 940 |
+
getEdgeHandleAtPosition(x, y, tolerance = 8) {
|
| 941 |
+
if (this.selectedBoxIndex >= 0 && this.annotations[this.selectedBoxIndex].type === 'segmentation') {
|
| 942 |
+
const points = this.annotations[this.selectedBoxIndex].points;
|
| 943 |
for (let i = 0; i < points.length; i++) {
|
| 944 |
const nextIndex = (i + 1) % points.length;
|
| 945 |
const midX = (points[i].x + points[nextIndex].x) / 2;
|
| 946 |
const midY = (points[i].y + points[nextIndex].y) / 2;
|
| 947 |
|
| 948 |
+
const distance = Math.sqrt(Math.pow(x - midX, 2) + Math.pow(y - midY, 2));
|
| 949 |
+
if (distance <= tolerance) {
|
| 950 |
+
return i;
|
| 951 |
+
}
|
| 952 |
}
|
| 953 |
}
|
| 954 |
+
return -1;
|
| 955 |
+
}
|
| 956 |
+
|
| 957 |
+
// NEW METHOD: Resize polygon edge
|
| 958 |
+
resizePolygonEdge(annotationIndex, edgeIndex, mouseX, mouseY) {
|
| 959 |
+
if (annotationIndex < 0 || annotationIndex >= this.annotations.length) return;
|
| 960 |
+
|
| 961 |
+
const annotation = this.annotations[annotationIndex];
|
| 962 |
+
if (annotation.type !== 'segmentation') return;
|
| 963 |
+
|
| 964 |
+
const points = annotation.points;
|
| 965 |
+
const nextIndex = (edgeIndex + 1) % points.length;
|
| 966 |
+
|
| 967 |
+
// Get the edge vector
|
| 968 |
+
const edgeVectorX = points[nextIndex].x - points[edgeIndex].x;
|
| 969 |
+
const edgeVectorY = points[nextIndex].y - points[edgeIndex].y;
|
| 970 |
+
|
| 971 |
+
// Get the edge normal (perpendicular vector)
|
| 972 |
+
const edgeLength = Math.sqrt(edgeVectorX * edgeVectorX + edgeVectorY * edgeVectorY);
|
| 973 |
+
if (edgeLength === 0) return;
|
| 974 |
+
|
| 975 |
+
const normalX = -edgeVectorY / edgeLength;
|
| 976 |
+
const normalY = edgeVectorX / edgeLength;
|
| 977 |
+
|
| 978 |
+
// Get edge midpoint
|
| 979 |
+
const edgeMidX = (points[edgeIndex].x + points[nextIndex].x) / 2;
|
| 980 |
+
const edgeMidY = (points[edgeIndex].y + points[nextIndex].y) / 2;
|
| 981 |
+
|
| 982 |
+
// Calculate how far to move the edge
|
| 983 |
+
const mouseVectorX = mouseX - edgeMidX;
|
| 984 |
+
const mouseVectorY = mouseY - edgeMidY;
|
| 985 |
+
|
| 986 |
+
// Project mouse movement onto the normal
|
| 987 |
+
const moveDistance = mouseVectorX * normalX + mouseVectorY * normalY;
|
| 988 |
+
|
| 989 |
+
// Move both edge points
|
| 990 |
+
const newX1 = Math.max(0, Math.min(this.originalWidth, points[edgeIndex].x + normalX * moveDistance));
|
| 991 |
+
const newY1 = Math.max(0, Math.min(this.originalHeight, points[edgeIndex].y + normalY * moveDistance));
|
| 992 |
+
const newX2 = Math.max(0, Math.min(this.originalWidth, points[nextIndex].x + normalX * moveDistance));
|
| 993 |
+
const newY2 = Math.max(0, Math.min(this.originalHeight, points[nextIndex].y + normalY * moveDistance));
|
| 994 |
+
|
| 995 |
+
points[edgeIndex].x = newX1;
|
| 996 |
+
points[edgeIndex].y = newY1;
|
| 997 |
+
points[nextIndex].x = newX2;
|
| 998 |
+
points[nextIndex].y = newY2;
|
| 999 |
+
|
| 1000 |
+
annotation.saved = false;
|
| 1001 |
}
|
| 1002 |
|
| 1003 |
// Polygon-specific editing methods
|
|
|
|
| 1147 |
const pos = this.getMousePos(e);
|
| 1148 |
this.lastMouseX = pos.x;
|
| 1149 |
this.lastMouseY = pos.y;
|
| 1150 |
+
const handle = this.getResizeHandle(pos.x, pos.y);
|
| 1151 |
|
| 1152 |
if (this.annotationMode === 'segmentation') {
|
| 1153 |
+
// Check if clicking on an edge handle of selected polygon
|
| 1154 |
if (this.selectedBoxIndex >= 0) {
|
| 1155 |
+
const edgeHandleIndex = this.getEdgeHandleAtPosition(pos.x, pos.y);
|
| 1156 |
+
if (edgeHandleIndex >= 0) {
|
| 1157 |
+
this.isDraggingEdge = true;
|
| 1158 |
+
this.draggingEdgeIndex = edgeHandleIndex;
|
| 1159 |
+
this.canvas.style.cursor = handle.cursor;
|
| 1160 |
+
return;
|
| 1161 |
+
}
|
| 1162 |
+
|
| 1163 |
const pointIndex = this.getPolygonPointAtPosition(pos.x, pos.y);
|
| 1164 |
if (pointIndex >= 0) {
|
| 1165 |
this.isDraggingPoint = true;
|
| 1166 |
this.draggingPointIndex = pointIndex;
|
| 1167 |
+
this.canvas.style.cursor = handle.cursor;
|
| 1168 |
return;
|
| 1169 |
}
|
| 1170 |
|
| 1171 |
// Check if clicking on an edge to add a point
|
| 1172 |
const edgeIndex = this.getPolygonEdgeAtPosition(pos.x, pos.y);
|
| 1173 |
+
if (edgeIndex >= 0 && e.ctrlKey) {
|
| 1174 |
this.addPolygonPoint(this.selectedBoxIndex, edgeIndex, pos.x, pos.y);
|
| 1175 |
this.drawCanvas();
|
| 1176 |
return;
|
|
|
|
| 1184 |
this.isDragging = true;
|
| 1185 |
this.dragStartX = pos.x;
|
| 1186 |
this.dragStartY = pos.y;
|
| 1187 |
+
this.canvas.style.cursor = handle.cursor;
|
| 1188 |
this.updateSelectedBoxInfo();
|
| 1189 |
this.drawCanvas();
|
| 1190 |
return;
|
|
|
|
| 1211 |
|
| 1212 |
// Original bbox logic remains the same...
|
| 1213 |
if (this.selectedBoxIndex >= 0) {
|
|
|
|
| 1214 |
if (handle) {
|
| 1215 |
this.isResizing = true;
|
| 1216 |
this.resizeHandle = handle;
|
|
|
|
| 1225 |
this.isDragging = true;
|
| 1226 |
this.dragStartX = pos.x;
|
| 1227 |
this.dragStartY = pos.y;
|
| 1228 |
+
this.canvas.style.cursor = handle.cursor;
|
| 1229 |
this.updateSelectedBoxInfo();
|
| 1230 |
this.drawCanvas();
|
| 1231 |
return;
|
|
|
|
| 1273 |
const pos = this.getMousePos(e);
|
| 1274 |
|
| 1275 |
if (this.annotationMode === 'segmentation') {
|
| 1276 |
+
if (this.isDraggingEdge && this.selectedBoxIndex >= 0) {
|
| 1277 |
+
// Resize edge
|
| 1278 |
+
this.resizePolygonEdge(this.selectedBoxIndex, this.draggingEdgeIndex, pos.x, pos.y);
|
| 1279 |
+
this.drawCanvas();
|
| 1280 |
+
return;
|
| 1281 |
+
}
|
| 1282 |
+
|
| 1283 |
if (this.isDraggingPoint && this.selectedBoxIndex >= 0) {
|
| 1284 |
// Move individual point
|
| 1285 |
this.movePolygonPoint(this.selectedBoxIndex, this.draggingPointIndex, pos.x, pos.y);
|
|
|
|
| 1334 |
|
| 1335 |
onMouseUp() {
|
| 1336 |
if (this.annotationMode === 'segmentation') {
|
| 1337 |
+
this.isDraggingEdge = false;
|
| 1338 |
+
this.draggingEdgeIndex = -1;
|
| 1339 |
this.isDraggingPoint = false;
|
| 1340 |
this.draggingPointIndex = -1;
|
| 1341 |
this.isDragging = false;
|
|
|
|
| 1385 |
}
|
| 1386 |
updatePolygonCursor(x, y) {
|
| 1387 |
if (this.selectedBoxIndex >= 0 && this.annotations[this.selectedBoxIndex].type === 'segmentation') {
|
| 1388 |
+
const handle = this.getResizeHandle(x, y);
|
| 1389 |
+
// Check for edge handles first
|
| 1390 |
+
const edgeHandleIndex = this.getEdgeHandleAtPosition(x, y);
|
| 1391 |
+
if (edgeHandleIndex >= 0) {
|
| 1392 |
+
this.canvas.style.cursor = handle.cursor;
|
| 1393 |
+
return;
|
| 1394 |
+
}
|
| 1395 |
+
|
| 1396 |
const pointIndex = this.getPolygonPointAtPosition(x, y);
|
| 1397 |
if (pointIndex >= 0) {
|
| 1398 |
+
this.canvas.style.cursor = handle.cursor;
|
| 1399 |
return;
|
| 1400 |
}
|
| 1401 |
|
|
|
|
| 1406 |
}
|
| 1407 |
|
| 1408 |
if (this.isPointInPolygon(x, y, this.annotations[this.selectedBoxIndex].points)) {
|
| 1409 |
+
this.canvas.style.cursor = handle.cursor;
|
| 1410 |
return;
|
| 1411 |
}
|
| 1412 |
}
|
|
|
|
| 1437 |
}
|
| 1438 |
|
| 1439 |
getResizeHandle(x, y) {
|
| 1440 |
+
if (this.selectedBoxIndex < 0) return {cursor: 'move'};
|
| 1441 |
|
| 1442 |
const box = this.annotations[this.selectedBoxIndex];
|
| 1443 |
const handleSize = 8;
|
|
|
|
| 1459 |
return handle;
|
| 1460 |
}
|
| 1461 |
}
|
| 1462 |
+
return {cursor: 'move'};
|
| 1463 |
}
|
| 1464 |
|
| 1465 |
getBoxAtPosition(x, y) {
|