ROIDrawer.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. import Drawer from './drawer.js';
  2. class ROIDrawer extends Drawer {
  3. constructor(canvas) {
  4. super(canvas);
  5. this.currentState = 'end';
  6. this.points = [];
  7. this.polygons = [];
  8. this.MAX_POLYGON = 1;
  9. this.onDrawROIFinishedCallback = null;
  10. }
  11. _init() {
  12. this.context.lineWidth = 2;
  13. this.context.font = 'bold 20px Arial';
  14. this.canvas.addEventListener('click', (e) => {
  15. switch (this.currentState) {
  16. case 'begin':
  17. this.points.push(getMousePos(this.canvas, e));
  18. this.currentState = 'firstPoint';
  19. break;
  20. case 'firstPoint': // 防止初始点点两次
  21. break;
  22. case 'move':
  23. this.currentState = 'points';
  24. break;
  25. case 'points':
  26. let currentPoint = getMousePos(this.canvas, e);
  27. if ((currentPoint.x === this.points[this.points.length - 1].x) && (currentPoint.y === this.points[this.points.length - 1].y)) {
  28. return;
  29. }
  30. this.points.push(currentPoint);
  31. if (checkPolygon(this.points)) {
  32. this.points.pop();
  33. }
  34. //console.log(this.points);
  35. break;
  36. case 'end':
  37. //console.log(this.points)
  38. break;
  39. default:
  40. console.log('unknown state: ', this.currentState);
  41. break;
  42. }
  43. // this._drawPolygons(this.points, true);
  44. });
  45. this.canvas.addEventListener('contextmenu', (e) => {
  46. e.preventDefault();
  47. if(this.currentState === 'end') { //防止点击右键启动绘制
  48. return ;
  49. }
  50. if (this.points.length <= 2) {
  51. this._resetPoints();
  52. } else {
  53. if ((this.points[0].x !== this.points[this.points.length - 1].x) || (this.points[0].y !== this.points[this.points.length - 1].y)) {
  54. this.points.push(this.points[0]);
  55. }
  56. if (checkPolygon(this.points)) {
  57. this.points.pop();
  58. } else {
  59. this.currentState = 'end';
  60. this.polygons.push([...this.points]);
  61. this.onDrawROIFinishedCallback && this.onDrawROIFinishedCallback();
  62. if(this.polygons.length < this.MAX_POLYGON) {
  63. this._resetPoints();
  64. } else {
  65. this._drawPolygons([]);
  66. }
  67. }
  68. }
  69. });
  70. this.canvas.addEventListener('mousemove', (event) => {
  71. let pos = getMousePos(this.canvas, event);
  72. //console.log('mousemove');
  73. if (event.button === 0) {
  74. if (this.currentState === 'firstPoint') {
  75. this.currentState = 'move';
  76. this.points.push(pos);
  77. } else if (this.currentState === 'move') {
  78. this.points.pop();
  79. this.points.push(pos);
  80. }
  81. if (this.currentState !== 'end') {
  82. this.points.push(pos);
  83. this._drawPolygons(this.points);
  84. this.points.pop();
  85. } else {
  86. //this._drawPolygons(this.points);
  87. }
  88. if(this.currentState === 'begin') {
  89. this._drawText('区域' + (this.polygons.length + 1), pos.x, pos.y)
  90. }
  91. }
  92. });
  93. }
  94. getROIData() {
  95. if(this.currentState !== 'end') { //非完成状态
  96. return null;
  97. }
  98. let polygons = [];
  99. this.polygons.map((points, k) =>{
  100. let data = points.slice(0, points.length - 1);
  101. data.map((point, k) => {
  102. data[k] = this._to8191Coordinate(point, this.canvas);
  103. });
  104. polygons.push([...data]);
  105. });
  106. return polygons;
  107. }
  108. setROI(polygons) {
  109. let data = [];
  110. polygons.map((points, k) => {
  111. points.map((point, k) => {
  112. let result = this._toRealCoordinate(point.x, point.y);
  113. data[k] = {x: result[0], y: result[1]};
  114. });
  115. if(data.length) {
  116. data[data.length] = data[0];
  117. }
  118. this.polygons.push([...data]);
  119. data = [];
  120. });
  121. this.currentState = 'end';
  122. this.points = [];
  123. this._drawPolygons(this.points);
  124. }
  125. redrawROI() {
  126. this._drawPolygons(this.points);
  127. }
  128. reset() {
  129. this.currentState = 'begin';
  130. this.polygons = [];
  131. this.points.length = 0;
  132. this._drawPolygons(this.points);
  133. }
  134. setROIFinishedCallback(callback) {
  135. this.onDrawROIFinishedCallback = callback;
  136. }
  137. setPolygonNum(num) {
  138. this.MAX_POLYGON = num;
  139. }
  140. terminate() {
  141. this.clearCanvas();
  142. this.canvas.width = 0;
  143. this.canvas.height = 0;
  144. this.currentState = 'end';
  145. this.points.length = 0;
  146. }
  147. _drawPolygons(points) {
  148. this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
  149. this.polygons.map((points, k)=> {
  150. this._drawPolygon(points);
  151. });
  152. this._drawPolygon(points);
  153. }
  154. _drawPolygon(points) {
  155. if (!points.length) {
  156. return;
  157. }
  158. const end = this.currentState === 'end';
  159. //console.log(points)
  160. this.context.strokeStyle = end ? '#0000ff' : "#ffff00";
  161. this.context.beginPath();
  162. this.context.moveTo(points[0].x, points[0].y);
  163. for (let i = 1; i < points.length; i++) {
  164. this.context.lineTo(points[i].x, points[i].y);
  165. }
  166. if (end) {
  167. // draw is done, fill polygon with a color
  168. this.context.fillStyle = "rgba(255, 0, 0, 0.2)";
  169. this.context.fill();
  170. } else {
  171. this.context.stroke();
  172. }
  173. this.context.closePath();
  174. this.polygons.map((points, k)=> {
  175. this._drawText('区域' + (k + 1), points[0].x - 20, points[0].y - 10);
  176. });
  177. }
  178. _resetPoints() {
  179. this.currentState = 'begin';
  180. this.points.length = 0;
  181. this._drawPolygons(this.points);
  182. }
  183. _drawText(text, x, y) {
  184. this.context.beginPath();
  185. this.context.fillStyle = "rgba(255, 255, 0, 1)";
  186. this.context.fillText(text, x, y);
  187. this.context.closePath();
  188. }
  189. }
  190. function getMousePos(canvas, event) {
  191. var rect = canvas.getBoundingClientRect();
  192. var x = event.clientX - rect.left * (canvas.width / rect.width);
  193. var y = event.clientY - rect.top * (canvas.height / rect.height);
  194. //console.log("x:"+x+",y:"+y);
  195. return {x: x, y: y};
  196. }
  197. function checkPolygon(points) {
  198. for (let i = 0, length = points.length - 1; i < length; i++) {
  199. for (let j = i + 1, len = points.length - 1; j < len; j++) {
  200. let result = segmentsIntr(points[i], points[i + 1], points[j], points[j + 1]);
  201. if (result) {
  202. console.log('intersect:');
  203. console.log(result)
  204. return result;
  205. }
  206. }
  207. }
  208. return false;
  209. }
  210. function segmentsIntr(a, b, c, d) {
  211. // 三角形abc 面积的2倍
  212. var area_abc = (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x);
  213. // 三角形abd 面积的2倍
  214. var area_abd = (a.x - d.x) * (b.y - d.y) - (a.y - d.y) * (b.x - d.x);
  215. // 面积符号相同则两点在线段同侧,不相交 (对点在线段上的情况,本例当作不相交处理);
  216. if (area_abc * area_abd >= 0) {
  217. return false;
  218. }
  219. // 三角形cda 面积的2倍
  220. var area_cda = (c.x - a.x) * (d.y - a.y) - (c.y - a.y) * (d.x - a.x);
  221. // 三角形cdb 面积的2倍
  222. // 注意: 这里有一个小优化.不需要再用公式计算面积,而是通过已知的三个面积加减得出.
  223. var area_cdb = area_cda + area_abc - area_abd;
  224. if (area_cda * area_cdb >= 0) {
  225. return false;
  226. }
  227. //计算交点坐标
  228. var t = area_cda / (area_abd - area_abc);
  229. var dx = t * (b.x - a.x),
  230. dy = t * (b.y - a.y);
  231. return {x: a.x + dx, y: a.y + dy};
  232. }
  233. export default ROIDrawer;