feat: 完成2D图表矩形框功能,对接Result Display字段
This commit is contained in:
		
							parent
							
								
									728e557006
								
							
						
					
					
						commit
						145b61d9c0
					
				|  | @ -46,44 +46,9 @@ import CustomChart from '@/components/CustomChart/index.vue' | |||
| import Custom3DChart from '@/components/Custom3DChart/index.vue' | ||||
| import ColorPalette from './ColorPalette.vue' | ||||
| import { getXAxisAndYAxisByPosition } from '@/utils/chartHelper.js' | ||||
| import * as echarts from 'echarts' | ||||
| 
 | ||||
| const buttons = ['2D', '3D Surface', '3D Scatter'] | ||||
| 
 | ||||
| function renderItem(params, api) { | ||||
|   console.log('%c [ params, api ]-54', 'font-size:13px; background:pink; color:#bf2c9f;', params, api) | ||||
|   const categoryIndex = api.value(0) | ||||
|   console.log('%c [ categoryIndex ]-56', 'font-size:13px; background:pink; color:#bf2c9f;', categoryIndex) | ||||
|   const start = api.coord([api.value(1), categoryIndex]) | ||||
|   console.log('%c [ start ]-58', 'font-size:13px; background:pink; color:#bf2c9f;', start) | ||||
|   const end = api.coord([api.value(2), categoryIndex]) | ||||
|   console.log('%c [ end ]-60', 'font-size:13px; background:pink; color:#bf2c9f;', end) | ||||
|   const height = api.size([0, 1])[1] * 0.6 | ||||
|   console.log('%c [ height ]-62', 'font-size:13px; background:pink; color:#bf2c9f;', height) | ||||
|   const rectShape = echarts.graphic.clipRectByRect( | ||||
|     { | ||||
|       x: start[0], | ||||
|       y: start[1] - height / 2, | ||||
|       width: end[0] - start[0], | ||||
|       height: height | ||||
|     }, | ||||
|     { | ||||
|       x: params.coordSys.x, | ||||
|       y: params.coordSys.y, | ||||
|       width: params.coordSys.width, | ||||
|       height: params.coordSys.height | ||||
|     } | ||||
|   ) | ||||
|   return ( | ||||
|     rectShape && { | ||||
|       type: 'rect', | ||||
|       transition: ['shape'], | ||||
|       shape: rectShape, | ||||
|       style: api.style() | ||||
|     } | ||||
|   ) | ||||
| } | ||||
| 
 | ||||
| // 2D 配置 | ||||
| const twoDOption = { | ||||
|   grid: { | ||||
|  | @ -166,29 +131,26 @@ const twoDOption = { | |||
|     max: 256, | ||||
|     interval: 64 | ||||
|   }, | ||||
|   series: [ | ||||
|     { | ||||
|       type: 'scatter', | ||||
|       symbolSize: 5, | ||||
|       data: [], | ||||
|       itemStyle: { | ||||
|         color: '#fff' | ||||
|       } | ||||
|   series: { | ||||
|     type: 'scatter', | ||||
|     symbolSize: 5, | ||||
|     data: [], | ||||
|     itemStyle: { | ||||
|       color: '#fff' | ||||
|     }, | ||||
|     { | ||||
|       type: 'custom', | ||||
|       renderItem: renderItem, | ||||
|       itemStyle: { | ||||
|         opacity: 0.8 | ||||
|       }, | ||||
|       encode: { | ||||
|         x: [1, 2], | ||||
|         y: 0 | ||||
|       }, | ||||
|       data: [[]] | ||||
|     markLine: { | ||||
|       silent: true, | ||||
|       symbol: 'none', | ||||
|       data: [], | ||||
|       animation: false, | ||||
|       lineStyle: { | ||||
|         type: 'solid', | ||||
|         width: 2 | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   brush: {} | ||||
|   }, | ||||
|   brush: {}, | ||||
|   animation: false | ||||
| } | ||||
| 
 | ||||
| //3D Surface 配置 | ||||
|  | @ -379,7 +341,7 @@ export default { | |||
|     handleChange(index) { | ||||
|       this.active = index | ||||
| 
 | ||||
|       // 因第一个二维的图表采用的v-show,而在该图表不显示的时候,且改变了浏览器大小触发resize时,大小会变为0, 故在切换回来的时候resize一下 | ||||
|       // 因第一个二维的图表采用的v-show(为了响应Unzoom事件),而在该图表不显示的时候,且改变了浏览器大小触发resize时,大小会变为0, 故在切换回来的时候resize一下 | ||||
|       if (this.active == 0) { | ||||
|         this.$nextTick(() => { | ||||
|           this.resize() | ||||
|  | @ -395,6 +357,7 @@ export default { | |||
|       this.twoDOption.yAxis.max = 256 | ||||
| 
 | ||||
|       this.emitRangeChange([0, 256, 0, 256]) | ||||
|       this.reDrawRect() | ||||
|     }, | ||||
| 
 | ||||
|     resize() { | ||||
|  | @ -457,19 +420,35 @@ export default { | |||
| 
 | ||||
|         const [x1, y2, x2, y1] = [...point1, ...point2] // 根据解析出的数据确定真实的范围 | ||||
| 
 | ||||
|         this.twoDOption.xAxis.min = x1 | ||||
|         this.twoDOption.xAxis.max = x2 | ||||
|         this.twoDOption.yAxis.min = y1 | ||||
|         this.twoDOption.yAxis.max = y2 | ||||
|         const rangeNumberFunc = this.rangeNumber(0, 256) | ||||
| 
 | ||||
|         this.twoDOption.xAxis.min = rangeNumberFunc(x1) | ||||
|         this.twoDOption.xAxis.max = rangeNumberFunc(x2) | ||||
|         this.twoDOption.yAxis.min = rangeNumberFunc(y1) | ||||
|         this.twoDOption.yAxis.max = rangeNumberFunc(y2) | ||||
| 
 | ||||
|         this.emitRangeChange([x1, x2, y1, y2]) | ||||
| 
 | ||||
|         this.reDrawRect() | ||||
|       } | ||||
| 
 | ||||
|       this.clearBrush(chart) | ||||
|     }, | ||||
| 
 | ||||
|     /** | ||||
|      * 限定数字在一定范围 | ||||
|      * @param {Number} min | ||||
|      * @param {Number} max | ||||
|      */ | ||||
|     rangeNumber(min, max) { | ||||
|       return num => { | ||||
|         return num > max ? max : num < min ? min : num | ||||
|       } | ||||
|     }, | ||||
| 
 | ||||
|     // 通知上层范围改变 | ||||
|     emitRangeChange(range) { | ||||
|       console.log('%c [ range ]-452', 'font-size:13px; background:pink; color:#bf2c9f;', range) | ||||
|       this.$emit('rangeChange', range) | ||||
|     }, | ||||
| 
 | ||||
|  | @ -489,7 +468,232 @@ export default { | |||
|       } | ||||
|     }, | ||||
| 
 | ||||
|     // 颜色插值 | ||||
|     // 重绘矩形框区域 | ||||
|     reDrawRect() { | ||||
|       const rectList = [] | ||||
| 
 | ||||
|       this.boundaryData.forEach(({ minX, maxX, minY, maxY, color }) => { | ||||
|         // rect 遵循 左下 右下 右上 左上 的顺序 | ||||
|         const rect = [ | ||||
|           [minX, minY], | ||||
|           [maxX, minY], | ||||
|           [maxX, maxY], | ||||
|           [minX, maxY] | ||||
|         ] | ||||
| 
 | ||||
|         rectList.push(...this.drawOneRect(rect, color)) | ||||
|       }) | ||||
|       this.twoDOption.series.markLine.data = rectList | ||||
|     }, | ||||
| 
 | ||||
|     /** | ||||
|      * 绘制一个矩形框区域 | ||||
|      * 矩形框在这里的实现是由几条线段围起来的,但由于线段在超出图表区域显示有问题,故作了以下处理 | ||||
|      * @param {*} rect  左下 右下 右上 左上 的顺序 | ||||
|      */ | ||||
|     drawOneRect(rect, color) { | ||||
|       const rectList = [] | ||||
|       const { | ||||
|         xAxis: { min: minX, max: maxX }, | ||||
|         yAxis: { min: minY, max: maxY } | ||||
|       } = this.twoDOption | ||||
| 
 | ||||
|       const inchartPoints = this.getInChartPoints(rect) | ||||
|       const outchartPoints = rect.filter(pointItem => !inchartPoints.includes(pointItem)) | ||||
|       // 如果框选范围内只有俩点 | ||||
|       if (inchartPoints.length == 2) { | ||||
|         const [point1, point2] = inchartPoints | ||||
|         const isVerticleLine = this.isVerticleLine(point1, point2) | ||||
|         // 如果是纵向标记线,判断另两个点是在左边还是右边 | ||||
|         if (isVerticleLine) { | ||||
|           const find = outchartPoints.find(outcharPoint => point1[1] == outcharPoint[1]) // 找出纵坐标相同的在图表外面的点 | ||||
|           // 判断在图表外的这个点是在左边还是右边 | ||||
|           const isLeft = find[0] <= point1[0] | ||||
|           /** | ||||
|            * 如果在左边,推入左边俩点构成矩形 | ||||
|            * y | ||||
|            * |________________ | ||||
|            * |                | | ||||
|            * |________________| | ||||
|            * | | ||||
|            * |——————————————————— x | ||||
|            **/ | ||||
| 
 | ||||
|           if (isLeft) { | ||||
|             inchartPoints.forEach(point => { | ||||
|               rectList.push(this.generateLineDataByTwoPoints([minX, point[1]], point)) | ||||
|             }) | ||||
| 
 | ||||
|             rectList.push(this.generateLineDataByTwoPoints(point1, point2)) | ||||
|           } | ||||
|           // 如果是右边,同理,推入右边俩点构成矩形 | ||||
|           else { | ||||
|             inchartPoints.forEach(point => { | ||||
|               rectList.push(this.generateLineDataByTwoPoints([maxX, point[1]], point)) | ||||
|             }) | ||||
|             rectList.push(this.generateLineDataByTwoPoints(point1, point2)) | ||||
|           } | ||||
|         } | ||||
|         // 如果是纵向标记线,判断另两个点是在上边还是下边 | ||||
|         else { | ||||
|           const find = outchartPoints.find(outcharPoint => point1[0] == outcharPoint[0]) // 找出横坐标相同的在图表外面的点 | ||||
|           // 判断在图表外的这个点是在上边还是右边 | ||||
|           const isBottom = find[1] <= point1[1] | ||||
|           /** | ||||
|            * 如果在下边,推入下边俩点构成矩形 | ||||
|            **/ | ||||
| 
 | ||||
|           if (isBottom) { | ||||
|             inchartPoints.forEach(point => { | ||||
|               rectList.push(this.generateLineDataByTwoPoints([point[0], minY], point)) | ||||
|             }) | ||||
| 
 | ||||
|             rectList.push(this.generateLineDataByTwoPoints(point1, point2)) | ||||
|           } | ||||
|           // 如果是上边,同理,推入上边俩点构成矩形 | ||||
|           else { | ||||
|             inchartPoints.forEach(point => { | ||||
|               rectList.push(this.generateLineDataByTwoPoints([point[0], maxY], point)) | ||||
|             }) | ||||
| 
 | ||||
|             rectList.push(this.generateLineDataByTwoPoints(point1, point2)) | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       // 只有一个点在范围内,则是选中了矩形的一个角 | ||||
|       else if (inchartPoints.length == 1) { | ||||
|         const point = inchartPoints[0] | ||||
|         const isLeft = !!outchartPoints.find(outPoint => outPoint[0] < point[0]) | ||||
|         const isBottom = !!outchartPoints.find(outPoint => outPoint[1] < point[1]) | ||||
|         // 截取的右上角 | ||||
|         if (isLeft && isBottom) { | ||||
|           rectList.push(this.generateLineDataByTwoPoints(point, [minX, point[1]])) | ||||
|           rectList.push(this.generateLineDataByTwoPoints(point, [point[0], minY])) | ||||
|         } | ||||
|         // 截取的右下角 | ||||
|         if (isLeft && !isBottom) { | ||||
|           rectList.push(this.generateLineDataByTwoPoints(point, [minX, point[1]])) | ||||
|           rectList.push(this.generateLineDataByTwoPoints(point, [point[0], maxY])) | ||||
|         } | ||||
|         // 截取的左下角 | ||||
|         if (!isLeft && !isBottom) { | ||||
|           rectList.push(this.generateLineDataByTwoPoints(point, [maxX, point[1]])) | ||||
|           rectList.push(this.generateLineDataByTwoPoints(point, [point[0], maxY])) | ||||
|         } | ||||
|         // 截取的左上角 | ||||
|         if (!isLeft && isBottom) { | ||||
|           rectList.push(this.generateLineDataByTwoPoints(point, [maxX, point[1]])) | ||||
|           rectList.push(this.generateLineDataByTwoPoints(point, [point[0], minY])) | ||||
|         } | ||||
|       } | ||||
|       // 全在里面 | ||||
|       else if (inchartPoints.length == 4) { | ||||
|         // 按顺序挨个连起来,并且尾部连到头部 | ||||
|         rect.forEach((point, index) => { | ||||
|           if (index == rect.length - 1) { | ||||
|             rectList.push(this.generateLineDataByTwoPoints(point, rect[0])) | ||||
|           } else { | ||||
|             rectList.push(this.generateLineDataByTwoPoints(point, rect[index + 1])) | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|       // 全不在里面 | ||||
|       else { | ||||
|         // 筛选出所有的在框选范围内的横坐标 | ||||
|         const xAxisList = rect.map(item => item[0]).filter(xAxis => xAxis > minX && xAxis < maxX) | ||||
|         const leftBottomPoint = rect[0] | ||||
|         const rightBottomPoint = rect[1] | ||||
|         const rightTopPoint = rect[3] | ||||
|         const minYAxis = rightBottomPoint[1] | ||||
|         const maYAxis = rightTopPoint[1] | ||||
|         // 需要显示左右两侧的框线 | ||||
|         if (xAxisList.length == 4 && minYAxis < minY && maYAxis > maxY) { | ||||
|           const minAxis = Math.min(...xAxisList) | ||||
|           const maxAxis = Math.max(...xAxisList) | ||||
|           rectList.push(this.generateLineDataByTwoPoints([minAxis, minY], [minAxis, maxY])) | ||||
|           rectList.push(this.generateLineDataByTwoPoints([maxAxis, minY], [maxAxis, maxY])) | ||||
|         } | ||||
|         // 需要显示左右其中一条框线 | ||||
|         else if (xAxisList.length == 2 && minYAxis < minY && maYAxis > maxY) { | ||||
|           const xAxis = xAxisList[0] | ||||
|           rectList.push(this.generateLineDataByTwoPoints([xAxis, minY], [xAxis, maxY])) | ||||
|         } | ||||
| 
 | ||||
|         // 筛选出所有的在框选范围内的横坐标 | ||||
|         const yAxisList = rect.map(item => item[1]).filter(xAxis => xAxis > minY && xAxis < maxY) | ||||
|         const minXAxis = leftBottomPoint[0] | ||||
|         const maxXAxis = rightBottomPoint[0] | ||||
|         // 需要显示上下两侧的框线 | ||||
|         if (yAxisList.length == 4 && minXAxis < minX && maxXAxis > maxX) { | ||||
|           const minAxis = Math.min(...yAxisList) | ||||
|           const maxAxis = Math.max(...yAxisList) | ||||
|           rectList.push(this.generateLineDataByTwoPoints([minX, minAxis], [maxX, minAxis])) | ||||
|           rectList.push(this.generateLineDataByTwoPoints([minX, maxAxis], [maxX, maxAxis])) | ||||
|         } | ||||
|         // 需要显示左右其中一条框线 | ||||
|         else if (yAxisList.length == 2 && minXAxis < minX && maxXAxis > maxX) { | ||||
|           const yAxis = yAxisList[0] | ||||
|           rectList.push(this.generateLineDataByTwoPoints([minX, yAxis], [maxX, yAxis])) | ||||
|         } | ||||
|       } | ||||
|       // 补齐颜色 | ||||
|       rectList.forEach(item => { | ||||
|         item[0].lineStyle = { | ||||
|           color | ||||
|         } | ||||
|       }) | ||||
|       return rectList | ||||
|     }, | ||||
| 
 | ||||
|     /** | ||||
|      * 获取在框选范围内的点列表 | ||||
|      * @param { Array<Array<number>> } rectInfo | ||||
|      */ | ||||
|     getInChartPoints(rectInfo) { | ||||
|       const { | ||||
|         xAxis: { min: minX, max: maxX }, | ||||
|         yAxis: { min: minY, max: maxY } | ||||
|       } = this.twoDOption | ||||
| 
 | ||||
|       return rectInfo.filter(point => { | ||||
|         const [xAxis, yAxis] = point | ||||
|         return xAxis >= minX && xAxis <= maxX && yAxis >= minY && yAxis <= maxY | ||||
|       }) | ||||
|     }, | ||||
| 
 | ||||
|     /** | ||||
|      * 根据俩点判断是横向还是纵向 | ||||
|      * x坐标相同,则是纵向,否则横向 | ||||
|      */ | ||||
|     isVerticleLine(point1, point2) { | ||||
|       return point1[0] == point2[0] ? true : false | ||||
|     }, | ||||
| 
 | ||||
|     /** | ||||
|      * 根据两个点生成一个markLine直线 | ||||
|      */ | ||||
|     generateLineDataByTwoPoints(point1, point2) { | ||||
|       return [ | ||||
|         { | ||||
|           xAxis: point1[0], | ||||
|           yAxis: point1[1] | ||||
|         }, | ||||
|         { | ||||
|           xAxis: point2[0], | ||||
|           yAxis: point2[1] | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
| 
 | ||||
|     // 随机颜色算法 | ||||
|     getRandomColor() { | ||||
|       const r = Math.floor(Math.random() * 256) // 0 到 255 之间的整数 | ||||
|       const g = Math.floor(Math.random() * 256) | ||||
|       const b = Math.floor(Math.random() * 256) | ||||
|       return `rgb(${r}, ${g}, ${b})` | ||||
|     }, | ||||
| 
 | ||||
|     // 颜色插值算法 | ||||
|     interpolateColor(color1, color2, percentage) { | ||||
|       const r = color1.r + (color2.r - color1.r) * percentage | ||||
|       const g = color1.g + (color2.g - color1.g) * percentage | ||||
|  | @ -502,7 +706,7 @@ export default { | |||
|     histogramDataList: { | ||||
|       handler(newVal) { | ||||
|         this.active = 0 | ||||
|         this.twoDOption.series[0].data = newVal.filter(item => item.c).map(item => [item.b, item.g, item.c]) // 设置2D Scatter数据 | ||||
|         this.twoDOption.series.data = newVal.filter(item => item.c).map(item => [item.b, item.g, item.c]) // 设置2D Scatter数据 | ||||
|       }, | ||||
|       immediate: true | ||||
|     }, | ||||
|  | @ -525,8 +729,11 @@ export default { | |||
|     // 2D 图表 上的 矩形 | ||||
|     boundary: { | ||||
|       handler(newVal) { | ||||
|         console.log('%c [  ]-462', 'font-size:13px; background:pink; color:#bf2c9f;', newVal) | ||||
|         this.twoDOption.series[1].data = newVal.map(({ minX, minY, maxX, maxY }) => [minX, minY, maxX, maxY]) | ||||
|         newVal.forEach(item => { | ||||
|           item.color = this.getRandomColor() | ||||
|         }) | ||||
|         this.boundaryData = newVal | ||||
|         this.reDrawRect() | ||||
|       }, | ||||
|       immediate: true | ||||
|     }, | ||||
|  | @ -540,9 +747,9 @@ export default { | |||
|             val / this.maxCount | ||||
|           ) | ||||
| 
 | ||||
|           this.twoDOption.series[0].itemStyle.color = `rgb(${r}, ${g}, ${b})` | ||||
|           this.twoDOption.series.itemStyle.color = `rgb(${r}, ${g}, ${b})` | ||||
|         } else { | ||||
|           this.twoDOption.series[0].itemStyle.color = '#fff' | ||||
|           this.twoDOption.series.itemStyle.color = '#fff' | ||||
|         } | ||||
|       }, | ||||
|       immediate: true | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ | |||
|       </template> | ||||
|       <template slot="uncertainty" slot-scope="text"> | ||||
|         <div class="uncertainty color-box"> | ||||
|           {{ text }} | ||||
|           +/-{{ text }} | ||||
|         </div> | ||||
|       </template> | ||||
|       <template slot="mdc" slot-scope="text"> | ||||
|  | @ -34,7 +34,7 @@ | |||
|       </template> | ||||
|       <template slot="uncertainty" slot-scope="text"> | ||||
|         <div class="uncertainty color-box"> | ||||
|           {{ text }} | ||||
|           +/-{{ text }} | ||||
|         </div> | ||||
|       </template> | ||||
|       <template slot="mdc" slot-scope="text"> | ||||
|  | @ -61,13 +61,13 @@ const columns = [ | |||
|   }, | ||||
|   { | ||||
|     title: 'Isotope', | ||||
|     dataIndex: 'isotope', | ||||
|     dataIndex: 'nuclideName', | ||||
|     ellipsis: true, | ||||
|     width: 76 | ||||
|   }, | ||||
|   { | ||||
|     title: 'Concentration', | ||||
|     dataIndex: 'concentration', | ||||
|     dataIndex: 'conc', | ||||
|     scopedSlots: { | ||||
|       customRender: 'concentration' | ||||
|     }, | ||||
|  | @ -75,7 +75,7 @@ const columns = [ | |||
|   }, | ||||
|   { | ||||
|     title: 'Uncertainty', | ||||
|     dataIndex: 'uncertainty', | ||||
|     dataIndex: 'concErr', | ||||
|     scopedSlots: { | ||||
|       customRender: 'uncertainty' | ||||
|     }, | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user