kfb-view 2.1.19 → 2.2.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kfb-view",
3
3
  "description": "一个在线kfb的阅片软件",
4
- "version": "2.1.19",
4
+ "version": "2.2.1",
5
5
  "author": "qifeng.fan <qifeng.fan@hzztai.com>",
6
6
  "license": "MIT",
7
7
  "main": "lib/kfb-view.js",
@@ -12,9 +12,10 @@ import {
12
12
  baseNumber, getAngle, getDistance, getPoint,
13
13
  getRectPoint,
14
14
  isPointInMatrix,
15
- pointInOtherPoint, isPointInPolygon, pointsToRegion,
15
+ pointInOtherPoint, isPointInPolygon, pointsToRegion, isPointInLine,
16
16
  } from '../../util/calculate';
17
17
  import {
18
+ EVENT_ADD_POLYGON_POINT,
18
19
  EVENT_AREA_MOVE_END,
19
20
  EVENT_AREA_MOVE_START, EVENT_AREA_MOVING, EVENT_CANCEL_SELECT_LABEL,
20
21
  EVENT_DB_CLICK_LABEL,
@@ -48,12 +49,19 @@ export class Area extends ViewerCommon {
48
49
  /**
49
50
  * 画点
50
51
  * @param {Object} point
51
- * @param {Object=} config 参数,参见thumb
52
+ * @param {Boolean=} active 是否是激活状态
52
53
  */
53
- drawPoint(point, config) {
54
+ drawPoint(point, active) {
54
55
  this.thumb.draw(point, {
55
- ...config,
56
- strokeStyle: this.options.thumbColor,
56
+ radius: !active ?
57
+ this.options.thumb.radius :
58
+ this.options.thumb.activeRadius,
59
+ strokeStyle: !active ?
60
+ this.options.thumb.color :
61
+ this.options.thumb.activeColor,
62
+ fillStyle: !active ?
63
+ this.options.thumb.bgColor :
64
+ this.options.thumb.activeBgColor,
57
65
  });
58
66
  }
59
67
 
@@ -78,7 +86,11 @@ export class Area extends ViewerCommon {
78
86
  this.draging = false;
79
87
  let selectList = [];
80
88
  let selectLabel;
89
+ let prevSelectLabel = undefined;
81
90
  this.labelList.forEach((item) => {
91
+ if (item.select) {
92
+ prevSelectLabel = item;
93
+ }
82
94
  item.select = false;
83
95
  // 如果有可移动的点了,不做操作
84
96
  if (this.movePoint) {
@@ -202,13 +214,39 @@ export class Area extends ViewerCommon {
202
214
  }
203
215
  selectLabel ?
204
216
  this.$emit(EVENT_SELECT_LABEL, selectLabel) :
205
- !this.movePoint ? this.$emit(EVENT_CANCEL_SELECT_LABEL) : '';
217
+ !this.movePoint && prevSelectLabel ?
218
+ this.$emit(EVENT_CANCEL_SELECT_LABEL, prevSelectLabel) : '';
206
219
  this.change();
207
220
  }
208
221
 
209
222
  onCanvasDblClick({x, y}) {
210
223
  setTimeout(() => {
211
- this.$emit(EVENT_DB_CLICK_LABEL);
224
+ const label = this.labelList.find((item) => item.select);
225
+ if (label?.tool === POLYGON) {
226
+ const points = this.imageToViewerElementPoints(label.points);
227
+ let index = -1;
228
+ for (let i = 0, l = points.length, j = l - 1; i < l; j = i, i++) {
229
+ const s = points[i];
230
+ const t = points[j];
231
+ if (isPointInLine({x, y}, [s, t])) {
232
+ index = i;
233
+ break;
234
+ }
235
+ }
236
+ if (index > -1) {
237
+ const p = this.viewerElementToImageCoordinates(x, y);
238
+ label.points.splice(index, 0, {
239
+ ...p,
240
+ canMove: true,
241
+ });
242
+ this.$emit(EVENT_ADD_POLYGON_POINT, label);
243
+ this.change();
244
+ } else {
245
+ this.$emit(EVENT_DB_CLICK_LABEL, label);
246
+ }
247
+ } else {
248
+ this.$emit(EVENT_DB_CLICK_LABEL, label);
249
+ }
212
250
  });
213
251
  }
214
252
 
@@ -231,11 +269,12 @@ export class Area extends ViewerCommon {
231
269
 
232
270
  /**
233
271
  * 监听鼠标拖动事件
272
+ * @param {Object} position
273
+ * @param {number} position.x
274
+ * @param {number} position.y
234
275
  * @param {Object} e
235
- * @param {number} e.x
236
- * @param {number} e.y
237
276
  */
238
- onCanvasDrag({x, y}) {
277
+ onCanvasDrag({x, y}, e) {
239
278
  if (this.options.drag === false) return;
240
279
  if (!this.lastPoint) this.lastPoint = {x, y};
241
280
  const label = this.movePoint.label;
@@ -245,6 +284,7 @@ export class Area extends ViewerCommon {
245
284
  if (!isMove && !isResize) return;
246
285
  if (position === 4 && !isMove) return;
247
286
  if (position !== 4 && !isResize) return;
287
+ e.preventDefaultAction = true;
248
288
  const scale = this.getImageZoom(true) / label.scale;
249
289
  const tool = label.tool;
250
290
  const region = label.region;
@@ -422,53 +462,42 @@ export class Area extends ViewerCommon {
422
462
  this.lastPoint = {x, y};
423
463
  }
424
464
 
425
- /**
426
- * 监听鼠标按钮事件
427
- * @param {Object} e
428
- */
429
- onCanvasKey(e) {
430
- const {originalEvent} = e;
431
- if (originalEvent.code === 'Escape') {
432
- this.$emit(EVENT_CANCEL_SELECT_LABEL);
433
- this.change();
434
- }
435
- }
436
-
437
465
  /**
438
466
  * 绘制点
439
467
  * @param {Object} points
440
468
  * @param {string} type
441
469
  */
442
- drawThumbPoint(points, type) {
443
- if (this.options.thumb === false) {
470
+ drawThumbPoints(points, type) {
471
+ if (this.options.thumb.show === false) {
444
472
  return;
445
473
  }
446
- const thumbRadius = this.options.thumbRadius ?? 5;
474
+ const position = this.movePoint?.position;
447
475
  if (LINE_TYPES.includes(type)) {
448
- this.drawPoint(points[0], {thumbRadius});
476
+ this.drawPoint(points[0], position === 1);
449
477
  this.drawPoint({
450
478
  x: (points[0].x + points[1].x) / 2,
451
479
  y: (points[0].y + points[1].y) / 2,
452
- }, {thumbRadius: thumbRadius + 2});
453
- this.drawPoint(points[1], {thumbRadius});
480
+ }, position === 4);
481
+ this.drawPoint(points[1], position === 7);
454
482
  } else if (REGION_TYPES.includes(type)) {
455
- getRectPoint(points[0], points[1]).forEach((point) => {
456
- this.drawPoint(point, {thumbRadius});
483
+ getRectPoint(points[0], points[1]).forEach((point, i) => {
484
+ this.drawPoint(point, position === i);
457
485
  });
458
486
  } else if (type === POLYGON) {
459
- points.forEach((point) => {
487
+ points.forEach((point, i) => {
460
488
  if (point.canMove) {
461
- this.drawPoint(point, {thumbRadius});
489
+ this.drawPoint(point, position === i);
462
490
  }
463
491
  });
464
492
  } else if (type === FONT || type === DOT) {
465
- this.drawPoint(points[0], {thumbRadius});
493
+ this.drawPoint(points[0], position === 4);
466
494
  } else if (type === STAR || type === FLAG) {
467
495
  getRectPoint(points[0], points[1]).
468
- filter((item, index) => index === 0 || index === 2 || index === 4 ||
469
- index === 6 || index === 8).
470
- forEach((point) => {
471
- this.drawPoint(point, {thumbRadius});
496
+ forEach((point, index) => {
497
+ if (index === 0 || index === 2 || index === 4 ||
498
+ index === 6 || index === 8) {
499
+ this.drawPoint(point, position === index);
500
+ }
472
501
  });
473
502
  }
474
503
  }
@@ -489,17 +518,17 @@ export class Area extends ViewerCommon {
489
518
  region.x + region.width, region.y + region.height);
490
519
  const scale = this.getImageZoom(true) / label.scale;
491
520
  if (!NO_NORMAL_REGION_TYPES.includes(label.tool)) {
492
- this.drawThumbPoint([startPoint, endPoint], label.tool);
521
+ this.drawThumbPoints([startPoint, endPoint], label.tool);
493
522
  } else if (label.tool === FONT) {
494
523
  const fontWidth = this.getFontWidth(label.text, label.fontSize);
495
524
  const pt = getPoint(startPoint, {
496
525
  x: startPoint.x + fontWidth / 2 * scale,
497
526
  y: startPoint.y - label.fontSize / 2 * scale + 4,
498
527
  }, label.angle);
499
- this.drawThumbPoint([pt], label.tool);
528
+ this.drawThumbPoints([pt], label.tool);
500
529
  } else if (label.tool === STAR) {
501
530
  const dist = label.lineWidth * scale;
502
- this.drawThumbPoint([
531
+ this.drawThumbPoints([
503
532
  {
504
533
  x: startPoint.x - dist,
505
534
  y: startPoint.y - dist,
@@ -508,14 +537,14 @@ export class Area extends ViewerCommon {
508
537
  y: startPoint.y + dist,
509
538
  }], label.tool);
510
539
  } else if (label.tool === DOT) {
511
- this.drawThumbPoint([
540
+ this.drawThumbPoints([
512
541
  {
513
542
  x: startPoint.x,
514
543
  y: startPoint.y,
515
544
  }], label.tool);
516
545
  } else if (label.tool === FLAG) {
517
546
  const dist = label.lineWidth * scale;
518
- this.drawThumbPoint([
547
+ this.drawThumbPoints([
519
548
  {
520
549
  x: startPoint.x,
521
550
  y: startPoint.y - dist,
@@ -526,10 +555,10 @@ export class Area extends ViewerCommon {
526
555
  } else if (label.tool === POLYGON) {
527
556
  if (this.draging) {
528
557
  const points = this.imageToViewerElementPoints(label.points);
529
- this.drawThumbPoint(points, label.tool);
558
+ this.drawThumbPoints(points, label.tool);
530
559
  } else {
531
560
  const points = this.setPointsMove(label);
532
- this.drawThumbPoint(points, label.tool);
561
+ this.drawThumbPoints(points, label.tool);
533
562
  }
534
563
  }
535
564
  this[label.tool].setContent(this.canvas, {...label});
@@ -552,7 +581,7 @@ export class Area extends ViewerCommon {
552
581
  label.points[index].canMove = false;
553
582
  if (index === 0 || (!pointInOtherPoint(lastPoint, point, 30) &&
554
583
  !pointInOtherPoint(movePoints[0], point, 30)) ||
555
- (!label.isClose && index === movePoints.length - 1)) {
584
+ (index === movePoints.length - 1)) {
556
585
  lastPoint = point;
557
586
  point.canMove = true;
558
587
  label.points[index].canMove = true;
@@ -48,6 +48,7 @@ export class Board extends ViewerCommon {
48
48
  * @param {boolean} [options.measure=true] 绘画时显示信息
49
49
  * @param {boolean} [options.clear=true] 是否结束绘画时清空画布
50
50
  * @param {boolean} [options.isClose=true] 是否自动闭合曲线
51
+ * @param {boolean} [options.enableDbClickClose=true] 是否双击结束曲线绘制
51
52
  * @param {boolean} [options.once=false] 是否绘画一次后自动结束
52
53
  * @param {boolean} [options.isROI=false] 是否是感性区域
53
54
  * @param {boolean} [options.move=true] 是否可移动
@@ -74,7 +75,7 @@ export class Board extends ViewerCommon {
74
75
  measure: true,
75
76
  clear: true,
76
77
  isClose: true,
77
- showStartPoint: true,
78
+ enableDbClickClose: true,
78
79
  ...options,
79
80
  lineWidth: !options.lineWidth ?
80
81
  (this.tool === STAR ? 15 :
@@ -104,7 +105,8 @@ export class Board extends ViewerCommon {
104
105
  text: '',
105
106
  measure: true,
106
107
  clear: true,
107
- showStartPoint: true,
108
+ isClose: true,
109
+ enableDbClickClose: true,
108
110
  ...options,
109
111
  lineWidth: !options.lineWidth ?
110
112
  (this.tool === STAR ? 15 :
@@ -143,12 +145,15 @@ export class Board extends ViewerCommon {
143
145
  }
144
146
  this.clearCanvas();
145
147
  this[tool].startDraw();
146
- this[tool].draw(this.imageToViewerElementPoints(this.points));
148
+ const points = this.imageToViewerElementPoints(this.points);
149
+ this[tool].draw(points);
147
150
  this[tool].endDraw();
148
151
  if (tool === POLYGON) {
149
- const point = this.imageToViewerElementCoordinates(this.points[0].x,
150
- this.points[0].y);
151
- this[tool].drawPoint(point);
152
+ this[tool].drawPoints(points, {
153
+ radius: this.options.thumb.radius,
154
+ strokeStyle: this.options.thumb.color,
155
+ fillStyle: this.options.thumb.bgColor,
156
+ });
152
157
  }
153
158
  this.$emit(EVENT_START_PAINTING, this.getPaintOptions());
154
159
  }
@@ -171,17 +176,21 @@ export class Board extends ViewerCommon {
171
176
  this.points = [this.points[0] || point, point];
172
177
  } else if (tool === POLYGON) {
173
178
  this.points.push(point);
179
+ this.setEnablePoints(false);
174
180
  } else if (POINT_TYPES.includes(tool)) {
175
181
  this.points = [point, point];
176
182
  }
177
183
  this.clearCanvas();
178
184
  this[tool].startDraw();
179
- this[tool].draw(this.imageToViewerElementPoints(this.points));
185
+ const points = this.imageToViewerElementPoints(this.points);
186
+ this[tool].draw(points);
180
187
  this[tool].endDraw();
181
188
  if (tool === POLYGON) {
182
- const point = this.imageToViewerElementCoordinates(this.points[0].x,
183
- this.points[0].y);
184
- this[tool].drawPoint(point);
189
+ this[tool].drawPoints(points, {
190
+ radius: this.options.thumb.radius,
191
+ strokeStyle: this.options.thumb.color,
192
+ fillStyle: this.options.thumb.bgColor,
193
+ });
185
194
  }
186
195
  this.$emit(EVENT_IN_PAINTING, this.getPaintOptions());
187
196
  }
@@ -207,30 +216,60 @@ export class Board extends ViewerCommon {
207
216
  } else if (POINT_TYPES.includes(tool)) {
208
217
  this.points = [point, point];
209
218
  } else if (tool === POLYGON) {
210
- const points = this.imageToViewerElementPoints(this.points);
211
- let firstPoint = points[0];
212
- // 是否是闭合曲线
213
- const isClose = this[this.tool].options.isClose;
214
- if (isClose) {
215
- // 绘制曲线时,点小于5个,或者曲线不是闭合的
216
- if (points.length < 5 || !pointInOtherPoint(firstPoint, {x, y})) {
219
+ const firstPoint = this.imageToViewerElementCoordinates(this.points[0].x,
220
+ this.points[0].y);
221
+ const enableDbClickClose = this[this.tool].options.enableDbClickClose; // 是否启用双击结束绘制
222
+ const isClose = this[this.tool].options.isClose; // 是否自动关闭
223
+ if (enableDbClickClose) {
224
+ if (isClose && pointInOtherPoint(firstPoint, {x, y})) {
225
+ this.setEnablePoints();
226
+ if (this.points.length < 5) {
227
+ return;
228
+ }
229
+ } else {
217
230
  return;
218
231
  }
219
- } else if (points.length < 5) {
232
+ } else {
233
+ this.setEnablePoints();
234
+ // 如果点小于5个,任务标注不符合规范
235
+ if (this.points.length < 5) {
236
+ this.points = [];
237
+ this.clearCanvas();
238
+ return;
239
+ }
240
+ }
241
+ }
242
+ this.$emit(EVENT_END_PAINTING, this.getPaintOptions());
243
+ // 是否只绘制一次
244
+ if (this[this.tool].options.once) {
245
+ this.endDraw();
246
+ } else if (this[this.tool].options.clear) { // 是否结束绘画时清空画布
247
+ this.points = [];
248
+ this.clearCanvas();
249
+ }
250
+ }
251
+
252
+ /**
253
+ * 监听鼠标双击事件
254
+ * @param {Object} e
255
+ * @param {number} e.x
256
+ * @param {number} e.y
257
+ */
258
+ onCanvasDblClick({x, y}) {
259
+ const point = this.viewerElementToImageCoordinates(x, y);
260
+ if (!this.isInROI(point) || this.points.length === 0) {
261
+ this.points = [];
262
+ this.clearCanvas();
263
+ return;
264
+ }
265
+ if (this.tool === POLYGON) {
266
+ this.setEnablePoints();
267
+ // 如果点小于5个,任务标注不符合规范
268
+ if (this.points.length < 5) {
269
+ this.points = [];
270
+ this.clearCanvas();
220
271
  return;
221
272
  }
222
- // 过滤曲线的点,点之间的距离小于10默认为一个点,取第一个
223
- this.points = this.points.filter((p, index) => {
224
- const _p = this.imageToViewerElementCoordinates(p.x, p.y);
225
- if (index === 0) return true;
226
- if (!isClose && index === this.points.length - 1) return true;
227
- if (pointInOtherPoint(firstPoint, _p)) {
228
- return false;
229
- } else {
230
- firstPoint = _p;
231
- return true;
232
- }
233
- });
234
273
  }
235
274
  this.$emit(EVENT_END_PAINTING, this.getPaintOptions());
236
275
  // 是否只绘制一次
@@ -257,14 +296,22 @@ export class Board extends ViewerCommon {
257
296
  }
258
297
  const tool = this.tool;
259
298
  if (tool === POLYGON && this.points.length > 0) {
260
- this.points.pop();
299
+ if (this.points.length > 1) {
300
+ this.points.pop();
301
+ }
261
302
  this.points.push(point);
262
303
  this.clearCanvas();
263
304
  this[tool].startDraw();
264
305
  const points = this.imageToViewerElementPoints(this.points);
265
306
  this[tool].draw(points);
266
307
  this[tool].endDraw();
267
- this[tool].drawPoint(points[0]);
308
+ if (tool === POLYGON) {
309
+ this[tool].drawPoints(points, {
310
+ radius: this.options.thumb.radius,
311
+ strokeStyle: this.options.thumb.color,
312
+ fillStyle: this.options.thumb.bgColor,
313
+ });
314
+ }
268
315
  this.$emit(EVENT_IN_PAINTING, this.getPaintOptions());
269
316
  }
270
317
  }
@@ -275,10 +322,43 @@ export class Board extends ViewerCommon {
275
322
  */
276
323
  onCanvasKey(e) {
277
324
  const {originalEvent} = e;
278
- e.preventDefaultAction = true;
279
- if (originalEvent.code === 'Escape') {
325
+ if (originalEvent.key === 'Escape') {
280
326
  this.endDraw();
281
327
  }
328
+ if (originalEvent.key === 'Delete' || originalEvent.key ===
329
+ 'Backspace') {
330
+ // 曲线绘制中,按删除键,删除上一个标注点
331
+ if (this.tool === POLYGON && this.points.length > 1) {
332
+ this.points.splice(this.points.length - 2, 1);
333
+ this.change();
334
+ }
335
+ }
336
+ }
337
+
338
+ setEnablePoints(end = true) {
339
+ const points = this.imageToViewerElementPoints(this.points);
340
+ let firstPoint = points[0];
341
+ // 过滤曲线的点,点之间的距离小于10默认为一个点,取第一个
342
+ this.points = this.points.filter((p, index) => {
343
+ const _p = points[index];
344
+ if (index === 0) return true;
345
+ if (index === this.points.length - 1) return true;
346
+ if (pointInOtherPoint(firstPoint, _p, 30)) {
347
+ return false;
348
+ } else {
349
+ firstPoint = _p;
350
+ return true;
351
+ }
352
+ });
353
+ if (end && this.points.length > 1) {
354
+ const lastPoint = this.points[this.points.length - 1];
355
+ const lastTwoPoint = this.points[this.points.length - 2];
356
+ if (pointInOtherPoint(
357
+ this.imageToViewerElementCoordinates(lastPoint.x, lastPoint.y),
358
+ this.imageToViewerElementCoordinates(lastTwoPoint.x, lastTwoPoint.y))) {
359
+ this.points.splice(this.points.length - 2, 1);
360
+ }
361
+ }
282
362
  }
283
363
 
284
364
  getPaintOptions() {
@@ -342,6 +422,13 @@ export class Board extends ViewerCommon {
342
422
  this[this.tool].startDraw();
343
423
  this[this.tool].draw(points);
344
424
  this[this.tool].endDraw();
425
+ if (this.tool === POLYGON) {
426
+ this[this.tool].drawPoints(points, {
427
+ radius: this.options.thumb.radius,
428
+ strokeStyle: this.options.thumb.color,
429
+ fillStyle: this.options.thumb.bgColor,
430
+ });
431
+ }
345
432
  this.$emit(EVENT_IN_PAINTING, this.getPaintOptions());
346
433
  }
347
434
  }
@@ -387,30 +387,20 @@ export class ViewerCommon extends EventEmitter {
387
387
  /**
388
388
  * 判断region是否在canvas中
389
389
  * @param {Object} region
390
- * @param {boolean} status
390
+ * @param {Object} bounds
391
391
  * @return {boolean}
392
392
  */
393
- isInCanvas(region, status = false) {
394
- const startPoint = status ?
395
- {x: region.x, y: region.y} :
396
- this.imageToViewerElementCoordinates(region.x, region.y);
397
- const endPoint = status ?
398
- {x: region.x + region.width, y: region.y + region.height} :
399
- this.imageToViewerElementCoordinates(
400
- region.x + region.width, region.y + region.height);
401
- if (endPoint.x < startPoint.x) {
402
- const x = startPoint.x;
403
- startPoint.x = endPoint.x;
404
- endPoint.x = x;
405
- }
406
- if (endPoint.y < startPoint.y) {
407
- const y = startPoint.y;
408
- startPoint.y = endPoint.y;
409
- endPoint.y = y;
410
- }
411
- return !(startPoint.x > this.canvas.width ||
412
- startPoint.y > this.canvas.height ||
413
- endPoint.x < 0 || endPoint.y < 0);
393
+ isInCanvas(region, bounds) {
394
+ const viewReact = this.viewport.imageToViewportRectangle(region.x, region.y,
395
+ region.width, region.height);
396
+ const startPoint = {x: viewReact.x, y: viewReact.y};
397
+ const endPoint = {
398
+ x: viewReact.x + viewReact.width,
399
+ y: viewReact.y + viewReact.height,
400
+ };
401
+ return !(startPoint.x > (bounds.x + bounds.width) || startPoint.y >
402
+ (bounds.y + bounds.height) || endPoint.x < bounds.x || endPoint.y <
403
+ bounds.y);
414
404
  }
415
405
 
416
406
  getFontWidth(text, size) {
@@ -91,7 +91,7 @@ class Rotation extends ViewerCommon {
91
91
  this.drawPoint({
92
92
  x: point.x,
93
93
  y: point.y,
94
- }, {thumbRadius: 5});
94
+ }, {radius: 5});
95
95
  });
96
96
  }
97
97
 
@@ -152,8 +152,7 @@ class Rotation extends ViewerCommon {
152
152
  */
153
153
  onCanvasKey(e) {
154
154
  const {originalEvent} = e;
155
- e.preventDefaultAction = true;
156
- if (originalEvent.code === 'Escape') {
155
+ if (originalEvent.key === 'Escape') {
157
156
  this.viewport.setRotation(0);
158
157
  this.stopRotation();
159
158
  }