kfb-view 2.1.16 → 2.1.19

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.16",
4
+ "version": "2.1.19",
5
5
  "author": "qifeng.fan <qifeng.fan@hzztai.com>",
6
6
  "license": "MIT",
7
7
  "main": "lib/kfb-view.js",
@@ -74,6 +74,7 @@ export class Board extends ViewerCommon {
74
74
  measure: true,
75
75
  clear: true,
76
76
  isClose: true,
77
+ showStartPoint: true,
77
78
  ...options,
78
79
  lineWidth: !options.lineWidth ?
79
80
  (this.tool === STAR ? 15 :
@@ -103,6 +104,7 @@ export class Board extends ViewerCommon {
103
104
  text: '',
104
105
  measure: true,
105
106
  clear: true,
107
+ showStartPoint: true,
106
108
  ...options,
107
109
  lineWidth: !options.lineWidth ?
108
110
  (this.tool === STAR ? 15 :
@@ -143,6 +145,11 @@ export class Board extends ViewerCommon {
143
145
  this[tool].startDraw();
144
146
  this[tool].draw(this.imageToViewerElementPoints(this.points));
145
147
  this[tool].endDraw();
148
+ if (tool === POLYGON) {
149
+ const point = this.imageToViewerElementCoordinates(this.points[0].x,
150
+ this.points[0].y);
151
+ this[tool].drawPoint(point);
152
+ }
146
153
  this.$emit(EVENT_START_PAINTING, this.getPaintOptions());
147
154
  }
148
155
 
@@ -171,6 +178,11 @@ export class Board extends ViewerCommon {
171
178
  this[tool].startDraw();
172
179
  this[tool].draw(this.imageToViewerElementPoints(this.points));
173
180
  this[tool].endDraw();
181
+ if (tool === POLYGON) {
182
+ const point = this.imageToViewerElementCoordinates(this.points[0].x,
183
+ this.points[0].y);
184
+ this[tool].drawPoint(point);
185
+ }
174
186
  this.$emit(EVENT_IN_PAINTING, this.getPaintOptions());
175
187
  }
176
188
 
@@ -249,8 +261,10 @@ export class Board extends ViewerCommon {
249
261
  this.points.push(point);
250
262
  this.clearCanvas();
251
263
  this[tool].startDraw();
252
- this[tool].draw(this.imageToViewerElementPoints(this.points));
264
+ const points = this.imageToViewerElementPoints(this.points);
265
+ this[tool].draw(points);
253
266
  this[tool].endDraw();
267
+ this[tool].drawPoint(points[0]);
254
268
  this.$emit(EVENT_IN_PAINTING, this.getPaintOptions());
255
269
  }
256
270
  }
@@ -71,29 +71,40 @@ export class ViewerCommon extends EventEmitter {
71
71
  * @return {boolean}
72
72
  */
73
73
  isContainerLabel(point, list) {
74
- const contain = list.find(({region, tool, points, isClose}) => {
75
- const p1 = {x: region.x, y: region.y};
76
- const p2 = {x: region.x + region.width, y: region.y};
77
- const p3 = {x: region.x, y: region.y + region.height};
78
- const p4 = {x: region.x + region.width, y: region.y + region.height};
79
- const center = {
80
- x: region.x + region.width / 2, y: region.y + region.height / 2,
81
- };
82
- if (tool === ELLIPSE) {
83
- return isPointInEllipse(point, center, Math.abs(region.width / 2),
84
- Math.abs(region.height / 2 >> 0));
85
- }
86
- if (tool === RECTANGLE) {
87
- return isPointInMatrix(p1, p2, p3, p4, point);
88
- }
89
- if (tool === POLYGON && isClose) {
90
- return isPointInPolygon(point, points);
91
- }
92
- return false;
93
- });
74
+ const contain = list.find((item) => this.isContainerInRegion(point, item));
94
75
  return !!contain;
95
76
  }
96
77
 
78
+ /**
79
+ * 判断点是否在某个区域内
80
+ * @param {Object} point
81
+ * @param {Object} region
82
+ * @param {String} tool
83
+ * @param {Array} points
84
+ * @param {boolean} isClose
85
+ * @return {boolean}
86
+ */
87
+ isContainerInRegion(point, {region, tool, points, isClose}) {
88
+ const p1 = {x: region.x, y: region.y};
89
+ const p2 = {x: region.x + region.width, y: region.y};
90
+ const p3 = {x: region.x, y: region.y + region.height};
91
+ const p4 = {x: region.x + region.width, y: region.y + region.height};
92
+ const center = {
93
+ x: region.x + region.width / 2, y: region.y + region.height / 2,
94
+ };
95
+ if (tool === ELLIPSE) {
96
+ return isPointInEllipse(point, center, Math.abs(region.width / 2),
97
+ Math.abs(region.height / 2 >> 0));
98
+ }
99
+ if (tool === RECTANGLE) {
100
+ return isPointInMatrix(p1, p2, p3, p4, point);
101
+ }
102
+ if (tool === POLYGON && isClose) {
103
+ return isPointInPolygon(point, points);
104
+ }
105
+ return false;
106
+ }
107
+
97
108
  /**
98
109
  * 转化图像区域到元素坐标区域
99
110
  * @param {number} x
@@ -149,10 +160,14 @@ export class ViewerCommon extends EventEmitter {
149
160
  * @return {{}[]}
150
161
  */
151
162
  imageToViewerElementPoints(points) {
152
- return (points || []).map((point) => ({
153
- ...point,
154
- ...this.imageToViewerElementCoordinates(point.x, point.y),
155
- }));
163
+ return (points || []).map((point) => {
164
+ const p = this.imageToViewerElementCoordinates(point.x, point.y);
165
+ return {
166
+ canMove: point.canMove,
167
+ x: p.x,
168
+ y: p.y,
169
+ };
170
+ });
156
171
  }
157
172
 
158
173
  /**
@@ -372,12 +387,17 @@ export class ViewerCommon extends EventEmitter {
372
387
  /**
373
388
  * 判断region是否在canvas中
374
389
  * @param {Object} region
390
+ * @param {boolean} status
375
391
  * @return {boolean}
376
392
  */
377
- isInCanvas(region) {
378
- const startPoint = this.imageToViewerElementCoordinates(region.x, region.y);
379
- const endPoint = this.imageToViewerElementCoordinates(
380
- region.x + region.width, region.y + region.height);
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);
381
401
  if (endPoint.x < startPoint.x) {
382
402
  const x = startPoint.x;
383
403
  startPoint.x = endPoint.x;
@@ -1,6 +1,6 @@
1
1
  import {
2
- HAS_REGION_TYPES, MARKS,
3
- NO_NORMAL_REGION_TYPES, POINT_TYPES, POLYGON, REGION_TYPES,
2
+ HAS_REGION_TYPES,
3
+ MARKS, NO_NORMAL_REGION_TYPES, POINT_TYPES, POLYGON, REGION_TYPES,
4
4
  } from '../../const/mark';
5
5
  import {ViewerCommon} from '../common/common';
6
6
  import * as Tools from '../../tool';
@@ -58,21 +58,34 @@ export class Shape extends ViewerCommon {
58
58
  change() {
59
59
  this.clearCanvas();
60
60
  // 区域标注列表
61
- const regionLabelList = this.labelList.filter(
62
- ({tool, isClose}) => REGION_TYPES.includes(tool) || isClose && tool ===
63
- POLYGON).
64
- map((item) => ({
65
- ...item,
66
- child: [],
67
- self: item,
68
- }));
61
+ const regionLabelList = [];
62
+ const polygonLabelList = [];
63
+ this.labelList.forEach(
64
+ (item) => {
65
+ if (item.tool === POLYGON) {
66
+ polygonLabelList.push(item);
67
+ }
68
+ if (REGION_TYPES.includes(item.tool) || item.isClose && item.tool ===
69
+ POLYGON) {
70
+ regionLabelList.push({
71
+ ...item,
72
+ child: [],
73
+ self: item,
74
+ });
75
+ }
76
+ });
69
77
  // strokeStyle, lineWidth, fillStyle 相同认为是一个路径下的图形,一起绘制,优化性能
70
78
  const sameFixWidthLabel = {}; // star, flag, star, font lineWidth固定为2
71
79
  const sameNormalLabel = {};
72
80
  this.labelList.forEach((item) => {
73
- if (!this.isInCanvas(item.region)) return;
74
81
  if (item.show === false) return;
75
82
  if (!this[item.tool]) return;
83
+ if (!this.isInCanvas(item.region)) return;
84
+ /* const point = this.imageToViewerElementCoordinates(item.points[0].x,
85
+ item.points[0].y);
86
+ if ((point.x > this.canvas.width ||
87
+ point.y > this.canvas.height ||
88
+ point.x < 0 || point.y < 0)) return;*/
76
89
  const key = `${item.strokeStyle ?? ''}_${item.lineWidth ??
77
90
  ''}_${item.fillStyle ?? ''}`;
78
91
  const regionKey = `${item.strokeStyle ?? ''}_${item.lineWidth ?? ''}`;
@@ -90,37 +103,48 @@ export class Shape extends ViewerCommon {
90
103
  }
91
104
  }
92
105
  const parent = regionLabelList.find(
93
- (parent) => parent.self !== item && item.points.every(
94
- (point) => this.isContainerLabel(point, [parent])));
106
+ (parent) => HAS_REGION_TYPES.includes(parent.tool) && parent.self !==
107
+ item && item.points.every(
108
+ (point) => this.isContainerInRegion(point, parent)));
95
109
  if (parent) {
96
110
  parent.child.push(item);
97
111
  }
98
112
  });
113
+ const ctx = this.canvas.getContext('2d');
99
114
  Object.values(sameFixWidthLabel).forEach((labels) => {
100
- const ctx = this.canvas.getContext('2d');
101
115
  ctx.beginPath();
102
116
  labels.forEach((item) => {
103
117
  this.drawLabel(item);
104
118
  });
105
- ctx.stroke();
106
- if (labels?.[0]?.fillStyle) {
119
+ const firstLabel = labels?.[0];
120
+ if (firstLabel?.fillStyle) {
121
+ ctx.fillStyle = firstLabel.fillStyle;
107
122
  ctx.fill();
108
123
  }
124
+ ctx.strokeStyle = firstLabel.strokeStyle;
125
+ ctx.stroke();
109
126
  });
110
127
  Object.values(sameNormalLabel).forEach((labels) => {
111
- const ctx = this.canvas.getContext('2d');
112
128
  ctx.beginPath();
113
129
  labels.forEach((item) => {
114
130
  this.drawLabel(item);
115
131
  });
132
+ const firstLabel = labels?.[0];
133
+ ctx.lineWidth = firstLabel.lineWidth;
134
+ ctx.strokeStyle = firstLabel.strokeStyle;
116
135
  ctx.stroke();
117
136
  });
118
137
 
119
138
  regionLabelList.forEach((item) => {
120
- if (!this.isInCanvas(item.region)) return;
121
139
  if (item.show === false) return;
122
140
  if (!this[item.tool]) return;
123
- this.combination.setContent(this.canvas, {...item});
141
+ if (!this.isInCanvas(item.region)) return;
142
+ /* const point = this.imageToViewerElementCoordinates(item.points[0].x,
143
+ item.points[0].y);
144
+ if ((point.x > this.canvas.width ||
145
+ point.y > this.canvas.height ||
146
+ point.x < 0 || point.y < 0)) return;*/
147
+ this.combination.setContent(this.canvas, item);
124
148
  this.combination.draw({
125
149
  points: this.imageToViewerElementPoints(item.points),
126
150
  tool: item.tool,
@@ -129,16 +153,26 @@ export class Shape extends ViewerCommon {
129
153
  tool,
130
154
  })), this.viewport.getRotation());
131
155
  });
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
+ });
132
166
  }
133
167
 
134
168
  drawLabel(item) {
135
- const scale = this.getImageZoom(true) / item.scale;
136
169
  const points = this.imageToViewerElementPoints(item.points);
137
- this[item.tool].setContent(this.canvas, {...item});
170
+ this[item.tool].setContent(this.canvas, item);
138
171
  if (!NO_NORMAL_REGION_TYPES.includes(item.tool)) {
139
172
  this[item.tool].draw(points, this.viewport.getRotation(),
140
173
  this.imageToViewerElementRectangle(item.region));
141
174
  } else {
175
+ const scale = this.getImageZoom(true) / item.scale;
142
176
  this[item.tool].draw(points, scale);
143
177
  }
144
178
  }
@@ -11,7 +11,7 @@ export class LabelModel {
11
11
  this.strokeStyle = data.strokeStyle || '#027AFF'; // 标注颜色
12
12
  this.fillStyle = data.fillStyle; // 填充颜色
13
13
  this.description = data.description; // 描述
14
- this.fontSize = data.fontSize; // 字号
14
+ this.fontSize = data.fontSize || 14; // 字号
15
15
  this.text = data.text; // 文字
16
16
  this.angle = data.angle ?? 0; // 旋转角度
17
17
  this.measure = data.measure; // 是否显示测量信息
@@ -23,6 +23,7 @@ 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; // 是否默认显示开始点,只针对曲线
26
27
  this.select = data.select ?? false; // 是否是选中状态
27
28
  this.show = data.show ?? true; // 是否显示
28
29
  this.__other__ = data.__other__ || {}; // 其他信息
package/src/tool/Brush.js CHANGED
@@ -21,9 +21,7 @@ class Brush {
21
21
  setContent(canvas, options) {
22
22
  this.canvas = canvas;
23
23
  this.thumb.setCanvas(canvas);
24
- this.options = {
25
- ...options,
26
- };
24
+ this.options = options;
27
25
  }
28
26
 
29
27
  startDraw() {
package/src/tool/Font.js CHANGED
@@ -24,13 +24,12 @@ class Font extends Brush {
24
24
  const angle = this.options.angle || 0;
25
25
  const text = this.options.text || '编辑文字';
26
26
  const ctx = this.canvas.getContext('2d');
27
- ctx.fillStyle = this.options.fillStyle || this.options.strokeStyle ||
28
- '#FDFDFD';
27
+ ctx.fillStyle = this.options.strokeStyle;
29
28
  ctx.save();
30
29
  ctx.translate(point.x, point.y);
31
30
  ctx.rotate(angle / 180 * Math.PI);
32
31
  ctx.scale(scale, scale);
33
- ctx.font = `${this.options.fontSize || 14}px Arial`;
32
+ ctx.font = `${this.options.fontSize}px Arial`;
34
33
  ctx.fillText(text, 0, 0);
35
34
  ctx.restore();
36
35
  }
@@ -26,6 +26,23 @@ class Polygon extends Brush {
26
26
  }
27
27
  }
28
28
  }
29
+
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);
40
+ this.thumb.draw(point, {
41
+ thumbRadius: this.options.thumbRadius ?? 5,
42
+ strokeStyle: this.options.thumbColor,
43
+ });
44
+ }
45
+ }
29
46
  }
30
47
 
31
48
  export {
package/src/tool/Star.js CHANGED
@@ -24,7 +24,6 @@ class Star extends Brush {
24
24
  let dist = this.options.lineWidth;
25
25
  const point = points[0];
26
26
  dist *= scale;
27
- this.endPoint = {x: point.x, y: point.y};
28
27
  const angle = 36 / 180 * Math.PI;
29
28
  const angle1 = 54 / 180 * Math.PI;
30
29
  const long_side = Math.sin(angle) * dist * 2;
@@ -60,7 +60,6 @@ CanvasRenderingContext2D.prototype.drawOval = function drawOval(
60
60
  this.save();
61
61
  // 选择a、b中的较大者作为arc方法的半径参数
62
62
  const r = (a > b) ? a : b;
63
- console.log(r);
64
63
  const ratioX = a / r; // 横轴缩放比率
65
64
  const ratioY = b / r; // 纵轴缩放比率
66
65
  this.scale(ratioX, ratioY); // 进行缩放(均匀压缩)
package/src/view.js CHANGED
@@ -96,7 +96,6 @@ export default class KfbView extends EventEmitter {
96
96
  this.labelList = list.map((item) => new LabelModel({
97
97
  ...item,
98
98
  region: item.region || pointsToRegion(item.points),
99
- strokeStyle: item.strokeStyle || '#0000FF',
100
99
  __data__: deepClone(item),
101
100
  }));
102
101
  this.board?.setLabelList?.(this.labelList);
@@ -109,7 +108,6 @@ export default class KfbView extends EventEmitter {
109
108
  ...this.labelList, ...list.map((item) => new LabelModel({
110
109
  ...item,
111
110
  region: item.region || pointsToRegion(item.points),
112
- strokeStyle: item.strokeStyle || '#0000FF',
113
111
  __data__: deepClone(item),
114
112
  }))];
115
113
  this.board?.setLabelList?.(this.labelList);
package/test.html CHANGED
@@ -7,7 +7,7 @@
7
7
  <body>
8
8
 
9
9
  <canvas id="canvas" width="800" height="600"></canvas>
10
-
10
+ <button id="reDraw">重绘</button>
11
11
  <script>
12
12
  /**
13
13
  * 清除圆形区域
@@ -206,109 +206,40 @@
206
206
  </script>
207
207
  <script>
208
208
  const canvas = document.getElementById('canvas');
209
+ const reDrawBtn = document.getElementById('reDraw');
209
210
  const ctx = canvas.getContext('2d');
210
- ctx.beginPath();
211
-
212
- drawA([{x: 10, y: 10}, {x: 50, y: 40}]);
213
- drawA([{x: 70, y: 70}, {x: 120, y: 120}]);
214
-
215
- drawB([{x: 130, y: 130}, {x: 90, y: 60}]);
216
- drawB([{x: 20, y: 20}, {x: 20, y: 90}]);
217
- drawF([{x: 300, y: 200}]);
218
- drawF([{x: 100, y: 500}]);
219
-
220
- function drawA(points) {
221
- const startPoint = points[0];
222
- const endPoint = points[points.length - 1];
223
- ctx.lineWidth = 2;
224
- ctx.strokeStyle = '#0000FF';
225
- ctx.moveTo(startPoint.x, startPoint.y);
226
- ctx.lineTo(endPoint.x, endPoint.y);
227
- ctx.drawArrow(startPoint, endPoint);
228
- ctx.stroke();
229
- }
230
211
 
231
- function drawB(points) {
232
- const startPoint = points[0];
233
- const endPoint = points[points.length - 1];
234
- ctx.lineWidth = 2;
235
- ctx.strokeStyle = '#00FF00';
236
- ctx.moveTo(startPoint.x, startPoint.y);
237
- ctx.lineTo(endPoint.x, endPoint.y);
238
- ctx.drawArrow(startPoint, endPoint);
239
- ctx.drawArrow(endPoint, startPoint);
240
- ctx.stroke();
241
- }
242
-
243
- function drawF(points, scale = 1) {
244
- let dist = 30;
245
- dist *= scale;
212
+ function drwaP(points) {
213
+ let dist = 4 * 1;
246
214
  const point = points[0];
215
+ const ctx = canvas.getContext('2d');
216
+ const {x, y} = point;
247
217
  ctx.lineWidth = 2;
248
- ctx.strokeStyle = '#0000FF';
249
- ctx.moveTo(point.x, point.y);
250
- ctx.lineTo(point.x, point.y - dist);
251
- const _dist = dist / 5;
252
- const p1 = {
253
- x: point.x + _dist,
254
- y: point.y - dist,
255
- };
256
- const p2 = {
257
- x: point.x + _dist * 2,
258
- y: point.y - dist - _dist,
259
- };
260
- const p3 = {
261
- x: point.x + _dist * 4,
262
- y: point.y - dist + _dist,
263
- };
264
- const p4 = {
265
- x: point.x + dist,
266
- y: point.y - dist,
267
- };
268
- ctx.bezierCurve([p1, p2, p3, p4]);
269
- ctx.moveTo(p4.x, p4.y);
270
- ctx.lineTo(p4.x, p4.y + dist / 2);
271
- const p11 = {
272
- x: point.x + _dist,
273
- y: point.y - dist / 2,
274
- };
275
- const p22 = {
276
- x: point.x + _dist * 2,
277
- y: point.y - dist / 2 - _dist,
278
- };
279
- const p33 = {
280
- x: point.x + _dist * 4,
281
- y: point.y - dist / 2 + _dist,
282
- };
283
- const p44 = {
284
- x: point.x + dist,
285
- y: point.y - dist / 2,
286
- };
287
- ctx.bezierCurve([p11, p22, p33, p44]);
288
- ctx.moveTo(p1.x, p1.y);
289
- ctx.lineTo(p11.x, p11.y);
290
- ctx.stroke();
218
+ ctx.moveTo(x + dist, y);
219
+ ctx.arc(x, y, dist, 0, Math.PI * 2, !1);
291
220
  }
292
221
 
293
- console.log(new Date().getTime());
294
- console.time('draw');
222
+ ctx.beginPath();
295
223
  ctx.fillStyle = '#ff0000';
296
224
  ctx.strokeStyle = '#00ff00';
297
225
  ctx.lineWidth = 2;
298
- ctx.beginPath();
299
- for (let i = 0; i < 800; i++) {
300
- for (let j = 0; j < 600; j++) {
301
- if (i % 50 === 0 && j % 50 === 0) {
302
- ctx.moveTo(i + 4, j);
303
- ctx.arc(i, j, 4, 0, Math.PI * 2, !1);
304
- }
305
- }
226
+ for (let i = 0; i < 4000; i++) {
227
+ drwaP([{x: Math.random() * 800, y: Math.random() * 600}]);
306
228
  }
307
- ctx.fill();
308
229
  ctx.stroke();
309
- console.timeEnd('draw');
310
- console.log(new Date().getTime());
311
-
230
+ reDrawBtn.addEventListener('click', (e) => {
231
+ ctx.clearRect(0, 0, 800, 600);
232
+ console.time('draw');
233
+ ctx.beginPath();
234
+ ctx.fillStyle = '#ff0000';
235
+ ctx.strokeStyle = '#00ff00';
236
+ ctx.lineWidth = 2;
237
+ for (let i = 0; i < 10000; i++) {
238
+ drwaP([{x: Math.random() * 800, y: Math.random() * 600}]);
239
+ }
240
+ ctx.stroke();
241
+ console.timeEnd('draw');
242
+ });
312
243
 
313
244
  </script>
314
245
  </body>