import Drawer from './drawer.js'; class ROIDrawer extends Drawer { constructor(canvas) { super(canvas); this.currentState = 'end'; this.points = []; this.polygons = []; this.MAX_POLYGON = 1; this.onDrawROIFinishedCallback = null; } _init() { this.context.lineWidth = 2; this.context.font = 'bold 20px Arial'; this.canvas.addEventListener('click', (e) => { switch (this.currentState) { case 'begin': this.points.push(getMousePos(this.canvas, e)); this.currentState = 'firstPoint'; break; case 'firstPoint': // 防止初始点点两次 break; case 'move': this.currentState = 'points'; break; case 'points': let currentPoint = getMousePos(this.canvas, e); if ((currentPoint.x === this.points[this.points.length - 1].x) && (currentPoint.y === this.points[this.points.length - 1].y)) { return; } this.points.push(currentPoint); if (checkPolygon(this.points)) { this.points.pop(); } //console.log(this.points); break; case 'end': //console.log(this.points) break; default: console.log('unknown state: ', this.currentState); break; } // this._drawPolygons(this.points, true); }); this.canvas.addEventListener('contextmenu', (e) => { e.preventDefault(); if(this.currentState === 'end') { //防止点击右键启动绘制 return ; } if (this.points.length <= 2) { this._resetPoints(); } else { if ((this.points[0].x !== this.points[this.points.length - 1].x) || (this.points[0].y !== this.points[this.points.length - 1].y)) { this.points.push(this.points[0]); } if (checkPolygon(this.points)) { this.points.pop(); } else { this.currentState = 'end'; this.polygons.push([...this.points]); this.onDrawROIFinishedCallback && this.onDrawROIFinishedCallback(); if(this.polygons.length < this.MAX_POLYGON) { this._resetPoints(); } else { this._drawPolygons([]); } } } }); this.canvas.addEventListener('mousemove', (event) => { let pos = getMousePos(this.canvas, event); //console.log('mousemove'); if (event.button === 0) { if (this.currentState === 'firstPoint') { this.currentState = 'move'; this.points.push(pos); } else if (this.currentState === 'move') { this.points.pop(); this.points.push(pos); } if (this.currentState !== 'end') { this.points.push(pos); this._drawPolygons(this.points); this.points.pop(); } else { //this._drawPolygons(this.points); } if(this.currentState === 'begin') { this._drawText('区域' + (this.polygons.length + 1), pos.x, pos.y) } } }); } getROIData() { if(this.currentState !== 'end') { //非完成状态 return null; } let polygons = []; this.polygons.map((points, k) =>{ let data = points.slice(0, points.length - 1); data.map((point, k) => { data[k] = this._to8191Coordinate(point, this.canvas); }); polygons.push([...data]); }); return polygons; } setROI(polygons) { let data = []; polygons.map((points, k) => { points.map((point, k) => { let result = this._toRealCoordinate(point.x, point.y); data[k] = {x: result[0], y: result[1]}; }); if(data.length) { data[data.length] = data[0]; } this.polygons.push([...data]); data = []; }); this.currentState = 'end'; this.points = []; this._drawPolygons(this.points); } redrawROI() { this._drawPolygons(this.points); } reset() { this.currentState = 'begin'; this.polygons = []; this.points.length = 0; this._drawPolygons(this.points); } setROIFinishedCallback(callback) { this.onDrawROIFinishedCallback = callback; } setPolygonNum(num) { this.MAX_POLYGON = num; } terminate() { this.clearCanvas(); this.canvas.width = 0; this.canvas.height = 0; this.currentState = 'end'; this.points.length = 0; } _drawPolygons(points) { this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); this.polygons.map((points, k)=> { this._drawPolygon(points); }); this._drawPolygon(points); } _drawPolygon(points) { if (!points.length) { return; } const end = this.currentState === 'end'; //console.log(points) this.context.strokeStyle = end ? '#0000ff' : "#ffff00"; this.context.beginPath(); this.context.moveTo(points[0].x, points[0].y); for (let i = 1; i < points.length; i++) { this.context.lineTo(points[i].x, points[i].y); } if (end) { // draw is done, fill polygon with a color this.context.fillStyle = "rgba(255, 0, 0, 0.2)"; this.context.fill(); } else { this.context.stroke(); } this.context.closePath(); this.polygons.map((points, k)=> { this._drawText('区域' + (k + 1), points[0].x - 20, points[0].y - 10); }); } _resetPoints() { this.currentState = 'begin'; this.points.length = 0; this._drawPolygons(this.points); } _drawText(text, x, y) { this.context.beginPath(); this.context.fillStyle = "rgba(255, 255, 0, 1)"; this.context.fillText(text, x, y); this.context.closePath(); } } function getMousePos(canvas, event) { var rect = canvas.getBoundingClientRect(); var x = event.clientX - rect.left * (canvas.width / rect.width); var y = event.clientY - rect.top * (canvas.height / rect.height); //console.log("x:"+x+",y:"+y); return {x: x, y: y}; } function checkPolygon(points) { for (let i = 0, length = points.length - 1; i < length; i++) { for (let j = i + 1, len = points.length - 1; j < len; j++) { let result = segmentsIntr(points[i], points[i + 1], points[j], points[j + 1]); if (result) { console.log('intersect:'); console.log(result) return result; } } } return false; } function segmentsIntr(a, b, c, d) { // 三角形abc 面积的2倍 var area_abc = (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x); // 三角形abd 面积的2倍 var area_abd = (a.x - d.x) * (b.y - d.y) - (a.y - d.y) * (b.x - d.x); // 面积符号相同则两点在线段同侧,不相交 (对点在线段上的情况,本例当作不相交处理); if (area_abc * area_abd >= 0) { return false; } // 三角形cda 面积的2倍 var area_cda = (c.x - a.x) * (d.y - a.y) - (c.y - a.y) * (d.x - a.x); // 三角形cdb 面积的2倍 // 注意: 这里有一个小优化.不需要再用公式计算面积,而是通过已知的三个面积加减得出. var area_cdb = area_cda + area_abc - area_abd; if (area_cda * area_cdb >= 0) { return false; } //计算交点坐标 var t = area_cda / (area_abd - area_abc); var dx = t * (b.x - a.x), dy = t * (b.y - a.y); return {x: a.x + dx, y: a.y + dy}; } export default ROIDrawer;