kfb-view 2.1.19 → 2.1.20

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.1.20",
5
5
  "author": "qifeng.fan <qifeng.fan@hzztai.com>",
6
6
  "license": "MIT",
7
7
  "main": "lib/kfb-view.js",
@@ -78,7 +78,11 @@ export class Area extends ViewerCommon {
78
78
  this.draging = false;
79
79
  let selectList = [];
80
80
  let selectLabel;
81
+ let prevSelectLabel = undefined;
81
82
  this.labelList.forEach((item) => {
83
+ if (item.select) {
84
+ prevSelectLabel = item;
85
+ }
82
86
  item.select = false;
83
87
  // 如果有可移动的点了,不做操作
84
88
  if (this.movePoint) {
@@ -202,13 +206,15 @@ export class Area extends ViewerCommon {
202
206
  }
203
207
  selectLabel ?
204
208
  this.$emit(EVENT_SELECT_LABEL, selectLabel) :
205
- !this.movePoint ? this.$emit(EVENT_CANCEL_SELECT_LABEL) : '';
209
+ !this.movePoint && prevSelectLabel ?
210
+ this.$emit(EVENT_CANCEL_SELECT_LABEL, prevSelectLabel) : '';
206
211
  this.change();
207
212
  }
208
213
 
209
214
  onCanvasDblClick({x, y}) {
210
215
  setTimeout(() => {
211
- this.$emit(EVENT_DB_CLICK_LABEL);
216
+ const label = this.labelList.find((item) => item.select);
217
+ this.$emit(EVENT_DB_CLICK_LABEL, label);
212
218
  });
213
219
  }
214
220
 
@@ -428,8 +434,9 @@ export class Area extends ViewerCommon {
428
434
  */
429
435
  onCanvasKey(e) {
430
436
  const {originalEvent} = e;
431
- if (originalEvent.code === 'Escape') {
432
- this.$emit(EVENT_CANCEL_SELECT_LABEL);
437
+ const label = this.labelList.find((item) => item.select);
438
+ if (originalEvent.code === 'Escape' && label) {
439
+ this.$emit(EVENT_CANCEL_SELECT_LABEL, label);
433
440
  this.change();
434
441
  }
435
442
  }
@@ -552,7 +559,7 @@ export class Area extends ViewerCommon {
552
559
  label.points[index].canMove = false;
553
560
  if (index === 0 || (!pointInOtherPoint(lastPoint, point, 30) &&
554
561
  !pointInOtherPoint(movePoints[0], point, 30)) ||
555
- (!label.isClose && index === movePoints.length - 1)) {
562
+ (index === movePoints.length - 1)) {
556
563
  lastPoint = point;
557
564
  point.canMove = true;
558
565
  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,11 @@ 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);
152
153
  }
153
154
  this.$emit(EVENT_START_PAINTING, this.getPaintOptions());
154
155
  }
@@ -171,17 +172,17 @@ export class Board extends ViewerCommon {
171
172
  this.points = [this.points[0] || point, point];
172
173
  } else if (tool === POLYGON) {
173
174
  this.points.push(point);
175
+ this.setEnablePoints(false);
174
176
  } else if (POINT_TYPES.includes(tool)) {
175
177
  this.points = [point, point];
176
178
  }
177
179
  this.clearCanvas();
178
180
  this[tool].startDraw();
179
- this[tool].draw(this.imageToViewerElementPoints(this.points));
181
+ const points = this.imageToViewerElementPoints(this.points);
182
+ this[tool].draw(points);
180
183
  this[tool].endDraw();
181
184
  if (tool === POLYGON) {
182
- const point = this.imageToViewerElementCoordinates(this.points[0].x,
183
- this.points[0].y);
184
- this[tool].drawPoint(point);
185
+ this[tool].drawPoints(points);
185
186
  }
186
187
  this.$emit(EVENT_IN_PAINTING, this.getPaintOptions());
187
188
  }
@@ -207,30 +208,60 @@ export class Board extends ViewerCommon {
207
208
  } else if (POINT_TYPES.includes(tool)) {
208
209
  this.points = [point, point];
209
210
  } 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})) {
211
+ const firstPoint = this.imageToViewerElementCoordinates(this.points[0].x,
212
+ this.points[0].y);
213
+ const enableDbClickClose = this[this.tool].options.enableDbClickClose; // 是否启用双击结束绘制
214
+ const isClose = this[this.tool].options.isClose; // 是否自动关闭
215
+ if (enableDbClickClose) {
216
+ if (isClose && pointInOtherPoint(firstPoint, {x, y})) {
217
+ this.setEnablePoints();
218
+ if (this.points.length < 5) {
219
+ return;
220
+ }
221
+ } else {
222
+ return;
223
+ }
224
+ } else {
225
+ this.setEnablePoints();
226
+ // 如果点小于5个,任务标注不符合规范
227
+ if (this.points.length < 5) {
228
+ this.points = [];
229
+ this.clearCanvas();
217
230
  return;
218
231
  }
219
- } else if (points.length < 5) {
232
+ }
233
+ }
234
+ this.$emit(EVENT_END_PAINTING, this.getPaintOptions());
235
+ // 是否只绘制一次
236
+ if (this[this.tool].options.once) {
237
+ this.endDraw();
238
+ } else if (this[this.tool].options.clear) { // 是否结束绘画时清空画布
239
+ this.points = [];
240
+ this.clearCanvas();
241
+ }
242
+ }
243
+
244
+ /**
245
+ * 监听鼠标双击事件
246
+ * @param {Object} e
247
+ * @param {number} e.x
248
+ * @param {number} e.y
249
+ */
250
+ onCanvasDblClick({x, y}) {
251
+ const point = this.viewerElementToImageCoordinates(x, y);
252
+ if (!this.isInROI(point) || this.points.length === 0) {
253
+ this.points = [];
254
+ this.clearCanvas();
255
+ return;
256
+ }
257
+ if (this.tool === POLYGON) {
258
+ this.setEnablePoints();
259
+ // 如果点小于5个,任务标注不符合规范
260
+ if (this.points.length < 5) {
261
+ this.points = [];
262
+ this.clearCanvas();
220
263
  return;
221
264
  }
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
265
  }
235
266
  this.$emit(EVENT_END_PAINTING, this.getPaintOptions());
236
267
  // 是否只绘制一次
@@ -257,14 +288,18 @@ export class Board extends ViewerCommon {
257
288
  }
258
289
  const tool = this.tool;
259
290
  if (tool === POLYGON && this.points.length > 0) {
260
- this.points.pop();
291
+ if (this.points.length > 1) {
292
+ this.points.pop();
293
+ }
261
294
  this.points.push(point);
262
295
  this.clearCanvas();
263
296
  this[tool].startDraw();
264
297
  const points = this.imageToViewerElementPoints(this.points);
265
298
  this[tool].draw(points);
266
299
  this[tool].endDraw();
267
- this[tool].drawPoint(points[0]);
300
+ if (tool === POLYGON) {
301
+ this[tool].drawPoints(points);
302
+ }
268
303
  this.$emit(EVENT_IN_PAINTING, this.getPaintOptions());
269
304
  }
270
305
  }
@@ -274,10 +309,46 @@ export class Board extends ViewerCommon {
274
309
  * @param {Object} e
275
310
  */
276
311
  onCanvasKey(e) {
277
- const {originalEvent} = e;
278
- e.preventDefaultAction = true;
279
- if (originalEvent.code === 'Escape') {
280
- this.endDraw();
312
+ if (this.isInDraw) {
313
+ const {originalEvent} = e;
314
+ if (originalEvent.code === 'Escape') {
315
+ this.endDraw();
316
+ }
317
+ if (originalEvent.code === 'Delete' || originalEvent.key ===
318
+ 'Backspace') {
319
+ // 曲线绘制中,按删除键,删除上一个标注点
320
+ if (this.tool === POLYGON && this.points.length > 1) {
321
+ this.points.splice(this.points.length - 2, 1);
322
+ this.change();
323
+ }
324
+ }
325
+ }
326
+ }
327
+
328
+ setEnablePoints(end = true) {
329
+ const points = this.imageToViewerElementPoints(this.points);
330
+ let firstPoint = points[0];
331
+ // 过滤曲线的点,点之间的距离小于10默认为一个点,取第一个
332
+ this.points = this.points.filter((p, index) => {
333
+ const _p = points[index];
334
+ if (index === 0) return true;
335
+ if (index === this.points.length - 1) return true;
336
+ if (pointInOtherPoint(firstPoint, _p, 30)) {
337
+ return false;
338
+ } else {
339
+ firstPoint = _p;
340
+ return true;
341
+ }
342
+ });
343
+ if (end && this.points.length > 1) {
344
+ const lastPoint = this.points[this.points.length - 1];
345
+ const lastTwoPoint = this.points[this.points.length - 2];
346
+ if (pointInOtherPoint(
347
+ this.imageToViewerElementCoordinates(lastPoint.x, lastPoint.y),
348
+ this.imageToViewerElementCoordinates(lastTwoPoint.x, lastTwoPoint.y),
349
+ 30)) {
350
+ this.points.splice(this.points.length - 2, 1);
351
+ }
281
352
  }
282
353
  }
283
354
 
@@ -342,6 +413,9 @@ export class Board extends ViewerCommon {
342
413
  this[this.tool].startDraw();
343
414
  this[this.tool].draw(points);
344
415
  this[this.tool].endDraw();
416
+ if (this.tool === POLYGON) {
417
+ this[this.tool].drawPoints(points);
418
+ }
345
419
  this.$emit(EVENT_IN_PAINTING, this.getPaintOptions());
346
420
  }
347
421
  }
@@ -151,11 +151,12 @@ class Rotation extends ViewerCommon {
151
151
  * @param {Object} e
152
152
  */
153
153
  onCanvasKey(e) {
154
- const {originalEvent} = e;
155
- e.preventDefaultAction = true;
156
- if (originalEvent.code === 'Escape') {
157
- this.viewport.setRotation(0);
158
- this.stopRotation();
154
+ if (this.isInRotation) {
155
+ const {originalEvent} = e;
156
+ if (originalEvent.code === 'Escape') {
157
+ this.viewport.setRotation(0);
158
+ this.stopRotation();
159
+ }
159
160
  }
160
161
  }
161
162
 
@@ -59,12 +59,8 @@ export class Shape extends ViewerCommon {
59
59
  this.clearCanvas();
60
60
  // 区域标注列表
61
61
  const regionLabelList = [];
62
- const polygonLabelList = [];
63
62
  this.labelList.forEach(
64
63
  (item) => {
65
- if (item.tool === POLYGON) {
66
- polygonLabelList.push(item);
67
- }
68
64
  if (REGION_TYPES.includes(item.tool) || item.isClose && item.tool ===
69
65
  POLYGON) {
70
66
  regionLabelList.push({
@@ -153,16 +149,6 @@ export class Shape extends ViewerCommon {
153
149
  tool,
154
150
  })), this.viewport.getRotation());
155
151
  });
156
-
157
- polygonLabelList.forEach((item) => {
158
- if (item.show === false) return;
159
- if (!this[item.tool]) return;
160
- if (!this.isInCanvas(item.region)) return;
161
- const point = this.imageToViewerElementCoordinates(item.points[0].x,
162
- item.points[0].y);
163
- this[item.tool].setContent(this.canvas, item);
164
- this[item.tool].drawPoint(point);
165
- });
166
152
  }
167
153
 
168
154
  drawLabel(item) {
@@ -219,10 +219,11 @@ export class Tailoring extends ViewerCommon {
219
219
  * @param {Object} e
220
220
  */
221
221
  onCanvasKey(e) {
222
- const {originalEvent} = e;
223
- e.preventDefaultAction = true;
224
- if (originalEvent.code === 'Escape') {
225
- this.stopTailoring();
222
+ if (this.isInTailoring) {
223
+ const {originalEvent} = e;
224
+ if (originalEvent.code === 'Escape') {
225
+ this.stopTailoring();
226
+ }
226
227
  }
227
228
  }
228
229
 
@@ -23,7 +23,6 @@ export class LabelModel {
23
23
  this.resize = data.resize; // 是否可拖动大小
24
24
  this.isROI = data.isROI ?? false; // 是否是ROI
25
25
  this.isClose = data.isClose ?? true; // 是否是闭合标注
26
- this.showStartPoint = data.showStartPoint ?? true; // 是否默认显示开始点,只针对曲线
27
26
  this.select = data.select ?? false; // 是否是选中状态
28
27
  this.show = data.show ?? true; // 是否显示
29
28
  this.__other__ = data.__other__ || {}; // 其他信息
@@ -16,7 +16,8 @@ class Polygon extends Brush {
16
16
  ctx.lineWidth = this.options.lineWidth;
17
17
  ctx.strokeStyle = this.options.strokeStyle;
18
18
  ctx.moveTo(points[0].x, points[0].y);
19
- points.forEach((point) => {
19
+ points.forEach((point, index) => {
20
+ if (index === 0) return;
20
21
  ctx.lineTo(point.x, point.y);
21
22
  });
22
23
  if (this.options.isClose) {
@@ -27,21 +28,13 @@ class Polygon extends Brush {
27
28
  }
28
29
  }
29
30
 
30
- drawPoint(point) {
31
- if (this.options.showStartPoint) {
32
- const ctx = this.canvas.getContext('2d');
33
- ctx.beginPath();
34
- ctx.fillStyle = '#0000008d';
35
- ctx.fillRect(point.x - 20, point.y - 25, 40, 20);
36
- ctx.beginPath();
37
- ctx.font = `14px Arial`;
38
- ctx.fillStyle = '#ffffff';
39
- ctx.fillText('起点', point.x - 15, point.y - 10);
31
+ drawPoints(points) {
32
+ points.forEach((point) => {
40
33
  this.thumb.draw(point, {
41
34
  thumbRadius: this.options.thumbRadius ?? 5,
42
35
  strokeStyle: this.options.thumbColor,
43
36
  });
44
- }
37
+ });
45
38
  }
46
39
  }
47
40
 
package/src/view.js CHANGED
@@ -309,16 +309,21 @@ function initEvent(kv) {
309
309
  const {originalEvent} = e;
310
310
  handlerCacheEvent(originalEvent, kv);
311
311
  handlerLabelEvent(originalEvent, kv);
312
- kv.shape?.onCanvasKey?.(e);
313
312
  kv.area?.onCanvasKey?.(e);
313
+ kv.shape?.onCanvasKey?.(e);
314
314
  kv.board?.onCanvasKey?.(e);
315
315
  kv.tailoring?.onCanvasKey?.(e);
316
316
  kv.rotation?.onCanvasKey?.(e);
317
317
  }, 100));
318
318
 
319
319
  kv.viewer.addHandler('canvas-double-click', (e) => {
320
- kv.area?.onCanvasDblClick?.(e.position);
321
- kv.tailoring?.onCanvasDblClick?.(e.position);
320
+ if (kv.board?.isInDraw) {
321
+ kv.board.onCanvasDblClick(e.position);
322
+ } else if (kv.tailoring?.isInTailoring) {
323
+ kv.tailoring?.onCanvasDblClick?.(e.position);
324
+ } else {
325
+ kv.area?.onCanvasDblClick?.(e.position);
326
+ }
322
327
  });
323
328
 
324
329
  kv.viewer.addHandler('resize', (e) => {
@@ -331,6 +336,43 @@ function initEvent(kv) {
331
336
  kv.board.onCanvasMove(e);
332
337
  }
333
338
  }, true, kv.$el);
339
+
340
+ let isRightDown = false;
341
+ let dragPosition;
342
+
343
+ kv.viewer.canvas.addEventListener('contextmenu', function(event) {
344
+ if (kv.board?.isInDraw) {
345
+ event.preventDefault();
346
+ }
347
+ });
348
+
349
+ kv.viewer.addHandler('canvas-nonprimary-press', function(e) {
350
+ console.log('nonprimary press');
351
+ if (e.button === 2) {
352
+ isRightDown = true;
353
+ }
354
+ dragPosition = e.position.clone();
355
+ });
356
+
357
+ kv.viewer.addHandler('canvas-nonprimary-release', function(e) {
358
+ console.log('nonprimary release');
359
+ if (e.button === 2) {
360
+ isRightDown = false;
361
+ }
362
+ });
363
+
364
+ new openSeadragon.MouseTracker({
365
+ element: kv.viewer.canvas,
366
+ moveHandler: function(event) {
367
+ if (isRightDown && kv.board?.isInDraw) {
368
+ console.log('nonprimary drag');
369
+ let delta = event.position.minus(dragPosition);
370
+ dragPosition = event.position.clone();
371
+ kv.viewer.viewport.panBy(
372
+ kv.viewer.viewport.deltaPointsFromPixels(delta.negate()));
373
+ }
374
+ },
375
+ });
334
376
  }
335
377
 
336
378
  function handlerCacheEvent(e, kv) {