diff --git a/src/views/spectrumAnalysis/components/BetaGammaSpectrumChart.vue b/src/views/spectrumAnalysis/components/BetaGammaSpectrumChart.vue index 2536365..93e0cc9 100644 --- a/src/views/spectrumAnalysis/components/BetaGammaSpectrumChart.vue +++ b/src/views/spectrumAnalysis/components/BetaGammaSpectrumChart.vue @@ -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> } 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 diff --git a/src/views/spectrumAnalysis/components/ResultDisplay.vue b/src/views/spectrumAnalysis/components/ResultDisplay.vue index 6beb3ac..b777c8d 100644 --- a/src/views/spectrumAnalysis/components/ResultDisplay.vue +++ b/src/views/spectrumAnalysis/components/ResultDisplay.vue @@ -11,7 +11,7 @@