import Drawer from './drawer.js'; class ROIDrawer extends Drawer { constructor(props) { super(props); 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', this._onClick.bind(this)); this.canvas.addEventListener('contextmenu', this._oncontextmenu.bind(this)); this.canvas.addEventListener('mousemove', this._onmousemove.bind(this)); } _onClick(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); } _oncontextmenu(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]); if (this.polygons.length < this.MAX_POLYGON) { this._resetPoints(); } else { this.onDrawROIFinishedCallback && this.onDrawROIFinishedCallback(); this._drawPolygons([]); } } } } _onmousemove(e) { let pos = getMousePos(this.canvas, e); //console.log('mousemove'); if (e.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) { if (!polygons) { return; } let data = []; this.polygons = []; 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; this.canvas.removeEventListener('click', this._onClick); this.canvas.removeEventListener('contextmenu', this._oncontextmenu); this.canvas.removeEventListener('mousemove', this._onmousemove); } _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(); } } class StandingDrawer extends Drawer { constructor(props) { super(props); this.currentState = 'end'; this.points = []; this.onDrawROIFinishedCallback = null; } _init() { this.context.lineWidth = 2; this.context.font = 'bold 20px Arial'; this.canvas.addEventListener('click', this._onClick.bind(this)); this.canvas.addEventListener('contextmenu', this._oncontextmenu.bind(this)); this.canvas.addEventListener('mousemove', this._onmousemove.bind(this)); } _onClick(e) { let pos = getMousePos(this.canvas, e); switch (this.currentState) { case 'begin': this.points.push(pos); this.currentState = 'firstPoint'; break; case 'firstPoint': // 防止初始点点两次 break; case 'move': this.currentState = 'points'; break; case 'points': if (((pos.x === this.points[this.points.length - 1].x) && (pos.y === this.points[this.points.length - 1].y)) || this.points.length > 6) { return; } this.points.push(pos); let points = this.points.length > 4 ? this.points.slice(0, 4) : this.points; if (checkPolygon(points)) { this.points.pop(); } if (this.points.length === 6) { this.currentState = 'end'; this.onDrawROIFinishedCallback && this.onDrawROIFinishedCallback(); } this._drawArea(this.points); break; case 'end': break; default: console.log('unknown state: ', this.currentState); break; } } _oncontextmenu(e) { e.preventDefault(); } _onmousemove(e) { let pos = getMousePos(this.canvas, e); if (e.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._drawArea(this.points); this.points.pop(); } else { } // if(this.currentState === 'begin') { // this._drawText('区域' + (this.polygons.length + 1), pos.x, pos.y) // } } } _drawArea(points) { this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); let polygon = points.length > 4 ? points.slice(0, 4) : points; this._drawPolygon(polygon); let line = points.length > 4 ? points.slice(4) : []; this._drawLine(line); } _drawPolygon(points) { if (!points.length) { return; } this.context.strokeStyle = '#00ff00'; 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); } this.points.length >= 4 && this.context.lineTo(points[0].x, points[0].y); this.context.stroke(); this.context.closePath(); } _drawLine(points) { if (points.length !== 2) { return; } this.context.strokeStyle = '#0000ff'; this.context.beginPath(); this.context.moveTo(points[0].x, points[0].y); this.context.lineTo(points[1].x, points[1].y); this.context.stroke(); this.context.closePath(); } getROIData() { if (this.currentState !== 'end') { //非完成状态 return null; } let points = []; this.points.map((point, k) => { points.push(this._to8191Coordinate(point, this.canvas)); }); return points; } setROI(points) { if (!points) { return; } let data = []; points.map((point, k) => { let result = this._toRealCoordinate(point.x, point.y); data[k] = { x: result[0], y: result[1] }; }); this.points = [...data]; this._drawArea(data); this.currentState = 'end'; } redrawROI() { this._drawArea(this.points); } reset() { this.currentState = 'begin'; this.points.length = 0; this._drawArea(this.points); } setROIFinishedCallback(callback) { this.onDrawROIFinishedCallback = callback; } terminate() { this.clearCanvas(); this.canvas.width = 0; this.canvas.height = 0; this.currentState = 'end'; this.points.length = 0; this.canvas.removeEventListener('click', this._onClick); this.canvas.removeEventListener('contextmenu', this._oncontextmenu); this.canvas.removeEventListener('mousemove', this._onmousemove); } } class SpeedLine extends Drawer { constructor(canvas) { super(canvas); this.currentState = 'end'; this.MAXLINE = 2; this.points = []; this.onDrawROIFinishedCallback = null; } _init() { this.canvas.addEventListener('click', this._onClick.bind(this)); this.canvas.addEventListener('contextmenu', this._oncontextmenu.bind(this)); this.canvas.addEventListener('mousemove', this._onmousemove.bind(this)); } _onClick(e) { if (this.points.length === this.MAXLINE * 2) { return; } 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 (this.points.length === this.MAXLINE * 2) { if (this._checkLine(this.points)) { this.points.pop(); } else { this.currentState = 'end'; this._drawLines(this.points); this.onDrawROIFinishedCallback && this.onDrawROIFinishedCallback(); } } break; case 'end': break; default: console.log('unknown state: ', this.currentState); break; } } _onmousemove(e) { let pos = getMousePos(this.canvas, e); if (e.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.currentState !== 'begin') { this.points.push(pos); this._drawLines(this.points); this.points.pop(); } } } _oncontextmenu(e) { e.preventDefault(); } /** * * @param points [] x1,y1,x2,y2... * @private */ _checkLine(points) { for (let i = 0; i < points.length - 3; i = i + 2) { for (let j = i + 2; j < points.length - 1; j = j + 2) { 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; } _drawLines(points) { this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); if (!points.length) { return; } const end = this.currentState === 'end'; this.context.strokeStyle = end ? '#00ff00' : "#ffff00"; this.context.beginPath(); for (let i = 0; i < points.length - 1; i = i + 2) { this.context.moveTo(points[i].x, points[i].y); this.context.lineTo(points[i + 1].x, points[i + 1].y); } this.context.stroke(); this.context.closePath(); } getROIData() { if (this.currentState !== 'end') { //非完成状态 return null; } let data = this.points; data.map((point, k) => { data[k] = this._to8191Coordinate(point, this.canvas); }); return data; } setROI(data) { data.map((point, k) => { let result = this._toRealCoordinate(point.x, point.y); data[k] = { x: result[0], y: result[1] }; }); this.currentState = 'end'; this.points = data; this._drawLines(this.points); } redrawROI() { this._drawLines(this.points); } reset() { this.currentState = 'begin'; this.points.length = 0; console.log(this.points) this._drawLines(this.points); } setROIFinishedCallback(callback) { this.onDrawROIFinishedCallback = callback; } setLineNum(num) { this.MAXLINE = num; } terminate() { this.clearCanvas(); this.canvas.width = 0; this.canvas.height = 0; this.currentState = 'end'; this.points.length = 0; this.canvas.removeEventListener('click', this._onClick); this.canvas.removeEventListener('contextmenu', this._oncontextmenu); this.canvas.removeEventListener('mousemove', this._onmousemove); } } class BlankROIDrawer extends Drawer { constructor(canvas) { super(canvas); } getROIData() { } setROI() { } redrawROI() { } reset() { } terminate() { } } 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 { ROIDrawer, StandingDrawer, SpeedLine, BlankROIDrawer };