ROIDrawer.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. import Drawer from './drawer.js';
  2. class ROIDrawer extends Drawer {
  3. constructor(props) {
  4. super(props);
  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', this._onClick.bind(this));
  15. this.canvas.addEventListener('contextmenu', this._oncontextmenu.bind(this));
  16. this.canvas.addEventListener('mousemove', this._onmousemove.bind(this));
  17. }
  18. _onClick(e) {
  19. switch (this.currentState) {
  20. case 'begin':
  21. this.points.push(getMousePos(this.canvas, e));
  22. this.currentState = 'firstPoint';
  23. break;
  24. case 'firstPoint': // 防止初始点点两次
  25. break;
  26. case 'move':
  27. this.currentState = 'points';
  28. break;
  29. case 'points':
  30. let currentPoint = getMousePos(this.canvas, e);
  31. if ((currentPoint.x === this.points[this.points.length - 1].x) && (currentPoint.y === this.points[this.points.length - 1].y)) {
  32. return;
  33. }
  34. this.points.push(currentPoint);
  35. if (checkPolygon(this.points)) {
  36. this.points.pop();
  37. }
  38. //console.log(this.points);
  39. break;
  40. case 'end':
  41. //console.log(this.points)
  42. break;
  43. default:
  44. console.log('unknown state: ', this.currentState);
  45. break;
  46. }
  47. // this._drawPolygons(this.points, true);
  48. }
  49. _oncontextmenu(e) {
  50. e.preventDefault();
  51. if(this.currentState === 'end') { //防止点击右键启动绘制
  52. return ;
  53. }
  54. if (this.points.length <= 2) {
  55. this._resetPoints();
  56. } else {
  57. if ((this.points[0].x !== this.points[this.points.length - 1].x) || (this.points[0].y !== this.points[this.points.length - 1].y)) {
  58. this.points.push(this.points[0]);
  59. }
  60. if (checkPolygon(this.points)) {
  61. this.points.pop();
  62. } else {
  63. this.currentState = 'end';
  64. this.polygons.push([...this.points]);
  65. if(this.polygons.length < this.MAX_POLYGON) {
  66. this._resetPoints();
  67. } else {
  68. this.onDrawROIFinishedCallback && this.onDrawROIFinishedCallback();
  69. this._drawPolygons([]);
  70. }
  71. }
  72. }
  73. }
  74. _onmousemove(e) {
  75. let pos = getMousePos(this.canvas, e);
  76. //console.log('mousemove');
  77. if (e.button === 0) {
  78. if (this.currentState === 'firstPoint') {
  79. this.currentState = 'move';
  80. this.points.push(pos);
  81. } else if (this.currentState === 'move') {
  82. this.points.pop();
  83. this.points.push(pos);
  84. }
  85. if (this.currentState !== 'end') {
  86. this.points.push(pos);
  87. this._drawPolygons(this.points);
  88. this.points.pop();
  89. } else {
  90. //this._drawPolygons(this.points);
  91. }
  92. if(this.currentState === 'begin') {
  93. this._drawText('区域' + (this.polygons.length + 1), pos.x, pos.y)
  94. }
  95. }
  96. }
  97. getROIData() {
  98. if(this.currentState !== 'end') { //非完成状态
  99. return null;
  100. }
  101. let polygons = [];
  102. this.polygons.map((points, k) =>{
  103. let data = points.slice(0, points.length - 1);
  104. data.map((point, k) => {
  105. data[k] = this._to8191Coordinate(point, this.canvas);
  106. });
  107. polygons.push([...data]);
  108. });
  109. return polygons;
  110. }
  111. setROI(polygons) {
  112. if(!polygons) {
  113. return;
  114. }
  115. let data = [];
  116. this.polygons = [];
  117. polygons.map((points, k) => {
  118. points.map((point, k) => {
  119. let result = this._toRealCoordinate(point.x, point.y);
  120. data[k] = {x: result[0], y: result[1]};
  121. });
  122. if(data.length) {
  123. data[data.length] = data[0];
  124. }
  125. this.polygons.push([...data]);
  126. data = [];
  127. });
  128. this.currentState = 'end';
  129. this.points = [];
  130. this._drawPolygons(this.points);
  131. }
  132. redrawROI() {
  133. this._drawPolygons(this.points);
  134. }
  135. reset() {
  136. this.currentState = 'begin';
  137. this.polygons = [];
  138. this.points.length = 0;
  139. this._drawPolygons(this.points);
  140. }
  141. setROIFinishedCallback(callback) {
  142. this.onDrawROIFinishedCallback = callback;
  143. }
  144. setPolygonNum(num) {
  145. this.MAX_POLYGON = num;
  146. }
  147. terminate() {
  148. this.clearCanvas();
  149. this.canvas.width = 0;
  150. this.canvas.height = 0;
  151. this.currentState = 'end';
  152. this.points.length = 0;
  153. this.canvas.removeEventListener('click', this._onClick);
  154. this.canvas.removeEventListener('contextmenu', this._oncontextmenu);
  155. this.canvas.removeEventListener('mousemove', this._onmousemove);
  156. }
  157. _drawPolygons(points) {
  158. this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
  159. this.polygons.map((points, k)=> {
  160. this._drawPolygon(points);
  161. });
  162. this._drawPolygon(points);
  163. }
  164. _drawPolygon(points) {
  165. if (!points.length) {
  166. return;
  167. }
  168. const end = this.currentState === 'end';
  169. //console.log(points)
  170. this.context.strokeStyle = end ? '#0000ff' : "#ffff00";
  171. this.context.beginPath();
  172. this.context.moveTo(points[0].x, points[0].y);
  173. for (let i = 1; i < points.length; i++) {
  174. this.context.lineTo(points[i].x, points[i].y);
  175. }
  176. if (end) {
  177. // draw is done, fill polygon with a color
  178. this.context.fillStyle = "rgba(255, 0, 0, 0.2)";
  179. this.context.fill();
  180. } else {
  181. this.context.stroke();
  182. }
  183. this.context.closePath();
  184. this.polygons.map((points, k)=> {
  185. this._drawText('区域' + (k + 1), points[0].x - 20, points[0].y - 10);
  186. });
  187. }
  188. _resetPoints() {
  189. this.currentState = 'begin';
  190. this.points.length = 0;
  191. this._drawPolygons(this.points);
  192. }
  193. _drawText(text, x, y) {
  194. this.context.beginPath();
  195. this.context.fillStyle = "rgba(255, 255, 0, 1)";
  196. this.context.fillText(text, x, y);
  197. this.context.closePath();
  198. }
  199. }
  200. class StandingDrawer extends Drawer {
  201. constructor(props) {
  202. super(props);
  203. this.currentState = 'end';
  204. this.points = [];
  205. this.onDrawROIFinishedCallback = null;
  206. }
  207. _init() {
  208. this.context.lineWidth = 2;
  209. this.context.font = 'bold 20px Arial';
  210. this.canvas.addEventListener('click', this._onClick.bind(this));
  211. this.canvas.addEventListener('contextmenu', this._oncontextmenu.bind(this));
  212. this.canvas.addEventListener('mousemove', this._onmousemove.bind(this));
  213. }
  214. _onClick(e) {
  215. let pos = getMousePos(this.canvas, e);
  216. switch (this.currentState) {
  217. case 'begin':
  218. this.points.push(pos);
  219. this.currentState = 'firstPoint';
  220. break;
  221. case 'firstPoint': // 防止初始点点两次
  222. break;
  223. case 'move':
  224. this.currentState = 'points';
  225. break;
  226. case 'points':
  227. if (((pos.x === this.points[this.points.length - 1].x) && (pos.y === this.points[this.points.length - 1].y))
  228. || this.points.length > 6) {
  229. return;
  230. }
  231. this.points.push(pos);
  232. let points = this.points.length > 4 ? this.points.slice(0, 4) : this.points;
  233. if(checkPolygon(points)) {
  234. this.points.pop();
  235. }
  236. if(this.points.length === 6) {
  237. this.currentState = 'end';
  238. this.onDrawROIFinishedCallback && this.onDrawROIFinishedCallback();
  239. }
  240. this._drawArea(this.points);
  241. break;
  242. case 'end':
  243. break;
  244. default:
  245. console.log('unknown state: ', this.currentState);
  246. break;
  247. }
  248. }
  249. _oncontextmenu(e) {
  250. e.preventDefault();
  251. }
  252. _onmousemove(e) {
  253. let pos = getMousePos(this.canvas, e);
  254. if (e.button === 0) {
  255. if (this.currentState === 'firstPoint') {
  256. this.currentState = 'move';
  257. this.points.push(pos);
  258. } else if (this.currentState === 'move') {
  259. this.points.pop();
  260. this.points.push(pos);
  261. }
  262. if (this.currentState !== 'end') {
  263. this.points.push(pos);
  264. this._drawArea(this.points);
  265. this.points.pop();
  266. } else {
  267. }
  268. // if(this.currentState === 'begin') {
  269. // this._drawText('区域' + (this.polygons.length + 1), pos.x, pos.y)
  270. // }
  271. }
  272. }
  273. _drawArea(points) {
  274. this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
  275. let polygon = points.length > 4 ? points.slice(0, 4) : points;
  276. this._drawPolygon(polygon);
  277. let line = points.length > 4 ? points.slice(4) : [];
  278. this._drawLine(line);
  279. }
  280. _drawPolygon(points) {
  281. if(!points.length) {
  282. return;
  283. }
  284. this.context.strokeStyle = '#00ff00';
  285. this.context.beginPath();
  286. this.context.moveTo(points[0].x, points[0].y);
  287. for (let i = 1; i < points.length; i++) {
  288. this.context.lineTo(points[i].x, points[i].y);
  289. }
  290. this.points.length >= 4 && this.context.lineTo(points[0].x, points[0].y);
  291. this.context.stroke();
  292. this.context.closePath();
  293. }
  294. _drawLine(points) {
  295. if(points.length !== 2) {
  296. return;
  297. }
  298. this.context.strokeStyle = '#0000ff';
  299. this.context.beginPath();
  300. this.context.moveTo(points[0].x, points[0].y);
  301. this.context.lineTo(points[1].x, points[1].y);
  302. this.context.stroke();
  303. this.context.closePath();
  304. }
  305. getROIData() {
  306. if(this.currentState !== 'end') { //非完成状态
  307. return null;
  308. }
  309. let points = [];
  310. this.points.map((point, k) => {
  311. points.push(this._to8191Coordinate(point, this.canvas));
  312. });
  313. return points;
  314. }
  315. setROI(points) {
  316. if(!points) {
  317. return;
  318. }
  319. let data = [];
  320. points.map((point, k) => {
  321. let result = this._toRealCoordinate(point.x, point.y);
  322. data[k] = {x: result[0], y: result[1]};
  323. });
  324. this.points = [...data];
  325. this._drawArea(data);
  326. this.currentState = 'end';
  327. }
  328. redrawROI() {
  329. this._drawArea(this.points);
  330. }
  331. reset() {
  332. this.currentState = 'begin';
  333. this.points.length = 0;
  334. this._drawArea(this.points);
  335. }
  336. setROIFinishedCallback(callback) {
  337. this.onDrawROIFinishedCallback = callback;
  338. }
  339. terminate() {
  340. this.clearCanvas();
  341. this.canvas.width = 0;
  342. this.canvas.height = 0;
  343. this.currentState = 'end';
  344. this.points.length = 0;
  345. this.canvas.removeEventListener('click', this._onClick);
  346. this.canvas.removeEventListener('contextmenu', this._oncontextmenu);
  347. this.canvas.removeEventListener('mousemove', this._onmousemove);
  348. }
  349. }
  350. class SpeedLine extends Drawer {
  351. constructor(canvas) {
  352. super(canvas);
  353. this.currentState = 'end';
  354. this.MAXLINE = 2;
  355. this.points = [];
  356. this.onDrawROIFinishedCallback = null;
  357. }
  358. _init() {
  359. this.canvas.addEventListener('click', this._onClick.bind(this));
  360. this.canvas.addEventListener('contextmenu', this._oncontextmenu.bind(this));
  361. this.canvas.addEventListener('mousemove', this._onmousemove.bind(this));
  362. }
  363. _onClick(e) {
  364. if(this.points.length === this.MAXLINE * 2) {
  365. return;
  366. }
  367. switch (this.currentState) {
  368. case 'begin':
  369. this.points.push(getMousePos(this.canvas, e));
  370. this.currentState = 'firstPoint';
  371. break;
  372. case 'firstPoint': // 防止初始点点两次
  373. break;
  374. case 'move':
  375. this.currentState = 'points';
  376. break;
  377. case 'points':
  378. let currentPoint = getMousePos(this.canvas, e);
  379. if ((currentPoint.x === this.points[this.points.length - 1].x) && (currentPoint.y === this.points[this.points.length - 1].y)) {
  380. return;
  381. }
  382. this.points.push(currentPoint);
  383. if(this.points.length === this.MAXLINE * 2) {
  384. if (this._checkLine(this.points)) {
  385. this.points.pop();
  386. } else {
  387. this.currentState = 'end';
  388. this._drawLines(this.points);
  389. this.onDrawROIFinishedCallback && this.onDrawROIFinishedCallback();
  390. }
  391. }
  392. break;
  393. case 'end':
  394. break;
  395. default:
  396. console.log('unknown state: ', this.currentState);
  397. break;
  398. }
  399. }
  400. _onmousemove(e) {
  401. let pos = getMousePos(this.canvas, e);
  402. console.log(e);
  403. if (e.button === 0) {
  404. if (this.currentState === 'firstPoint') {
  405. this.currentState = 'move';
  406. this.points.push(pos);
  407. } else if (this.currentState === 'move') {
  408. this.points.pop();
  409. this.points.push(pos);
  410. }
  411. if (this.currentState !== 'end' && this.currentState !== 'begin') {
  412. this.points.push(pos);
  413. this._drawLines(this.points);
  414. this.points.pop();
  415. }
  416. }
  417. }
  418. _oncontextmenu(e) {
  419. e.preventDefault();
  420. }
  421. /**
  422. *
  423. * @param points [] x1,y1,x2,y2...
  424. * @private
  425. */
  426. _checkLine(points) {
  427. for (let i = 0; i < points.length - 3; i = i + 2) {
  428. for (let j = i + 2; j < points.length - 1; j = j + 2) {
  429. let result = segmentsIntr(points[i], points[i + 1], points[j], points[j + 1]);
  430. if (result) {
  431. console.log('intersect:');
  432. console.log(result)
  433. return result;
  434. }
  435. }
  436. }
  437. return false;
  438. }
  439. _drawLines(points) {
  440. this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
  441. if (!points.length) {
  442. return;
  443. }
  444. const end = this.currentState === 'end';
  445. this.context.strokeStyle = end ? '#00ff00' : "#ffff00";
  446. this.context.beginPath();
  447. for (let i = 0; i < points.length - 1; i = i + 2) {
  448. this.context.moveTo(points[i].x, points[i].y);
  449. this.context.lineTo(points[i + 1].x, points[i + 1].y);
  450. }
  451. this.context.stroke();
  452. this.context.closePath();
  453. }
  454. getROIData() {
  455. if(this.currentState !== 'end') { //非完成状态
  456. return null;
  457. }
  458. let data = this.points;
  459. data.map((point, k) => {
  460. data[k] = this._to8191Coordinate(point, this.canvas);
  461. });
  462. return data;
  463. }
  464. setROI(data) {
  465. data.map((point, k) => {
  466. let result = this._toRealCoordinate(point.x, point.y);
  467. data[k] = {x: result[0], y: result[1]};
  468. });
  469. this.currentState = 'end';
  470. this.points = data;
  471. this._drawLines(this.points);
  472. }
  473. redrawROI() {
  474. this._drawLines(this.points);
  475. }
  476. reset() {
  477. this.currentState = 'begin';
  478. this.points.length = 0;
  479. console.log(this.points)
  480. this._drawLines(this.points);
  481. }
  482. setROIFinishedCallback(callback) {
  483. this.onDrawROIFinishedCallback = callback;
  484. }
  485. setLineNum(num) {
  486. this.MAXLINE = num;
  487. }
  488. terminate() {
  489. this.clearCanvas();
  490. this.canvas.width = 0;
  491. this.canvas.height = 0;
  492. this.currentState = 'end';
  493. this.points.length = 0;
  494. this.canvas.removeEventListener('click', this._onClick);
  495. this.canvas.removeEventListener('contextmenu', this._oncontextmenu);
  496. this.canvas.removeEventListener('mousemove', this._onmousemove);
  497. }
  498. }
  499. class BlankROIDrawer extends Drawer {
  500. constructor(canvas) {
  501. super(canvas);
  502. }
  503. getROIData() {
  504. }
  505. setROI() {
  506. }
  507. redrawROI() {
  508. }
  509. reset() {
  510. }
  511. terminate() {
  512. }
  513. }
  514. function getMousePos(canvas, event) {
  515. var rect = canvas.getBoundingClientRect();
  516. var x = event.clientX - rect.left * (canvas.width / rect.width);
  517. var y = event.clientY - rect.top * (canvas.height / rect.height);
  518. //console.log("x:"+x+",y:"+y);
  519. return {x: x, y: y};
  520. }
  521. function checkPolygon(points) {
  522. for (let i = 0, length = points.length - 1; i < length; i++) {
  523. for (let j = i + 1, len = points.length - 1; j < len; j++) {
  524. let result = segmentsIntr(points[i], points[i + 1], points[j], points[j + 1]);
  525. if (result) {
  526. console.log('intersect:');
  527. console.log(result)
  528. return result;
  529. }
  530. }
  531. }
  532. return false;
  533. }
  534. function segmentsIntr(a, b, c, d) {
  535. // 三角形abc 面积的2倍
  536. var area_abc = (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x);
  537. // 三角形abd 面积的2倍
  538. var area_abd = (a.x - d.x) * (b.y - d.y) - (a.y - d.y) * (b.x - d.x);
  539. // 面积符号相同则两点在线段同侧,不相交 (对点在线段上的情况,本例当作不相交处理);
  540. if (area_abc * area_abd >= 0) {
  541. return false;
  542. }
  543. // 三角形cda 面积的2倍
  544. var area_cda = (c.x - a.x) * (d.y - a.y) - (c.y - a.y) * (d.x - a.x);
  545. // 三角形cdb 面积的2倍
  546. // 注意: 这里有一个小优化.不需要再用公式计算面积,而是通过已知的三个面积加减得出.
  547. var area_cdb = area_cda + area_abc - area_abd;
  548. if (area_cda * area_cdb >= 0) {
  549. return false;
  550. }
  551. //计算交点坐标
  552. var t = area_cda / (area_abd - area_abc);
  553. var dx = t * (b.x - a.x),
  554. dy = t * (b.y - a.y);
  555. return {x: a.x + dx, y: a.y + dy};
  556. }
  557. export {ROIDrawer, StandingDrawer, SpeedLine, BlankROIDrawer};