提交 d526b2c7 authored 作者: 祖铭松's avatar 祖铭松

feat: update

上级 e017baf8
...@@ -27,6 +27,7 @@ import { ...@@ -27,6 +27,7 @@ import {
setSilhouetteHeatmap, setSilhouetteHeatmap,
setSilhouetteLegend setSilhouetteLegend
} from '../utils/index'; } from '../utils/index';
import { config } from 'plotly.js-dist';
const chartRef = ref(null); const chartRef = ref(null);
...@@ -68,47 +69,22 @@ const generateChart = (chartData) => { ...@@ -68,47 +69,22 @@ const generateChart = (chartData) => {
.attr('viewBox', [0, 0, width, height]) .attr('viewBox', [0, 0, width, height])
.attr('style', 'max-width: 100%; height: auto; font: 10px sans-serif;'); .attr('style', 'max-width: 100%; height: auto; font: 10px sans-serif;');
// 最大值 最小值
const { maxNumber, minNumber } = setMaxMinNumber(chartData);
// color config
const colorConfig = setColor(minNumber, maxNumber);
let configObj = { let configObj = {
...ret,
svg, svg,
width, width,
height, height,
radius, radius,
margin, margin,
maxNumber,
minNumber,
dendWidth, dendWidth,
titleConfig, titleConfig,
xAxisLength, xAxisLength,
heatWidth, heatWidth,
heatHeight, heatHeight
color: colorConfig,
...ret
}; };
const rootConfig = getRootConfig(configObj, chartData); // Consensus matrix
configObj = { ...configObj, ...rootConfig }; generateHeatmap(configObj, chartData);
// heatmap
const heatmapRect = setHeatmap(configObj);
setTips(heatmapRect);
// Tree
setTree(configObj);
// axisLabel
setAxisTitle(configObj);
// legend
ret.show_legend && setLegend(configObj);
// label
setLabel(configObj);
// Basis // Basis
generageBasis(configObj); generageBasis(configObj);
// Consensus // Consensus
...@@ -122,6 +98,42 @@ const generateChart = (chartData) => { ...@@ -122,6 +98,42 @@ const generateChart = (chartData) => {
chartRef.value.appendChild(svgInstance.value); chartRef.value.appendChild(svgInstance.value);
}; };
// Consensus matrix
const generateHeatmap = (config, chartData) => {
// max/min number
const { maxNumber, minNumber } = setMaxMinNumber(chartData);
// root config
const { root, vertRoot, orderingx } = getRootConfig(config, chartData);
// color config
let colors = [
'#313695',
'#436FB1',
'#6BA2CB',
'#9BCCE2',
'#F0F9D8',
'#F0F9D8',
'#FEF0A9',
'#FDCD7E',
'#FA9C58',
'#EE613D',
'#D22B26',
'#A50026'
];
const color = setColor(minNumber, maxNumber, colors);
const configObj = { ...config, root, vertRoot, orderingx, maxNumber, minNumber, color };
// heatmap
const heatmapRect = setHeatmap(configObj);
setTips(heatmapRect);
// Tree
setTree(configObj);
// axisLabel
setAxisTitle(configObj);
// legend
configObj.show_legend && setLegend(configObj);
// label
setLabel(configObj);
};
// Basis // Basis
const generageBasis = (config) => { const generageBasis = (config) => {
const chartData = chartMockData.slice(0, 2); const chartData = chartMockData.slice(0, 2);
......
...@@ -131,7 +131,7 @@ export const setHeatmap = (config) => { ...@@ -131,7 +131,7 @@ export const setHeatmap = (config) => {
.selectAll('g') .selectAll('g')
.data(config.root.leaves()) .data(config.root.leaves())
.join('g') .join('g')
.attr('transform', (d, i) => `translate(0, ${d.x - config.radius})`) .attr('transform', (d, i) => `translate(0, ${config.radius * 2 * i})`)
.selectAll('rect') .selectAll('rect')
.data((d) => d.data.values.map((e, i) => d.data.values[config.orderingx[i]])) .data((d) => d.data.values.map((e, i) => d.data.values[config.orderingx[i]]))
.join('rect') .join('rect')
...@@ -245,10 +245,11 @@ export const setLegend = (config) => { ...@@ -245,10 +245,11 @@ export const setLegend = (config) => {
const legendData = [...Array.from({ length: config.maxNumber }, (item, i) => i)]; const legendData = [...Array.from({ length: config.maxNumber }, (item, i) => i)];
config.svg.append('g') config.svg.append('g')
.attr('transform', `translate(${config.dendWidth + config.xAxisLength * (config.radius * 2) + 80}, ${config.margin + config.dendWidth * 2})`)
.selectAll() .selectAll()
.data(legendData) .data(legendData)
.join('g') .join('g')
.attr('transform', (d, i, nodes) => `translate(${config.dendWidth + config.xAxisLength * (config.radius * 2) + 80}, ${config.height / 3 + (nodes.length / 2 - i - 1) * 18 - 60})`) .attr('transform', (d, i, nodes) => `translate(0, ${(nodes.length - i) * 18 - 18})`)
.call((g) => g.append('rect').attr('width', 18).attr('height', 18).attr('fill', config.color())) .call((g) => g.append('rect').attr('width', 18).attr('height', 18).attr('fill', config.color()))
.call((g) => .call((g) =>
g g
...@@ -264,12 +265,10 @@ export const setLegend = (config) => { ...@@ -264,12 +265,10 @@ export const setLegend = (config) => {
// Basis legend // Basis legend
export const setBasisLegend = (config) => { export const setBasisLegend = (config) => {
const legendData = [...Array.from({ length: config.maxNumber }, (item, i) => i)]; const legendData = [...Array.from({ length: config.maxNumber }, (item, i) => i)];
const textX = config.dendWidth + config.xAxisLength * (config.radius * 2) + 180 const textX = config.dendWidth + config.xAxisLength * (config.radius * 2) + 180
config.svg.append('text') config.svg.append('text')
.attr('x', textX) .attr('x', textX)
.attr('y', config.height / 3 - 110) .attr('y', config.margin + config.dendWidth * 2)
.attr('fill', '#333333') .attr('fill', '#333333')
.attr('font-size', '20px') .attr('font-size', '20px')
.attr('font-family', `${config.titleConfig.fontFamily}`) .attr('font-family', `${config.titleConfig.fontFamily}`)
...@@ -279,10 +278,11 @@ export const setBasisLegend = (config) => { ...@@ -279,10 +278,11 @@ export const setBasisLegend = (config) => {
.text('basis'); .text('basis');
config.svg.append('g') config.svg.append('g')
.attr('transform', `translate(${config.dendWidth + config.xAxisLength * (config.radius * 2) + 160}, ${config.margin + config.dendWidth * 2})`)
.selectAll() .selectAll()
.data(legendData) .data(legendData)
.join('g') .join('g')
.attr('transform', (d, i, nodes) => `translate(${config.dendWidth + config.xAxisLength * (config.radius * 2) + 160}, ${config.height / 3 + (nodes.length / 2 - i - 1) * 21 - 50})`) .attr('transform', (d, i, nodes) => `translate(0, ${i * 21 + 10})`)
.call((g) => g.append('rect').attr('width', 15).attr('height', 15).attr('fill', config.color())) .call((g) => g.append('rect').attr('width', 15).attr('height', 15).attr('fill', config.color()))
.call((g) => .call((g) =>
g g
...@@ -298,12 +298,11 @@ export const setBasisLegend = (config) => { ...@@ -298,12 +298,11 @@ export const setBasisLegend = (config) => {
// Consensus legend // Consensus legend
export const setConsensusLegend = (config) => { export const setConsensusLegend = (config) => {
const legendData = [...Array.from({ length: config.maxNumber }, (item, i) => i)]; const legendData = [...Array.from({ length: config.maxNumber }, (item, i) => i)];
const textX = config.dendWidth + config.xAxisLength * (config.radius * 2) + 200
const textX = config.dendWidth + config.xAxisLength * (config.radius * 2) + 205 let yRatio = 175;
config.svg.append('text') config.svg.append('text')
.attr('x', textX) .attr('x', textX)
.attr('y', config.height / 3 + 50) .attr('y', config.margin + config.dendWidth * 2 + yRatio)
.attr('fill', '#333333') .attr('fill', '#333333')
.attr('font-size', '20px') .attr('font-size', '20px')
.attr('font-family', `${config.titleConfig.fontFamily}`) .attr('font-family', `${config.titleConfig.fontFamily}`)
...@@ -313,10 +312,11 @@ export const setConsensusLegend = (config) => { ...@@ -313,10 +312,11 @@ export const setConsensusLegend = (config) => {
.text('consensus'); .text('consensus');
config.svg.append('g') config.svg.append('g')
.attr('transform', `translate(${config.dendWidth + config.xAxisLength * (config.radius * 2) + 160}, ${config.margin + config.dendWidth * 2 + yRatio})`)
.selectAll() .selectAll()
.data(legendData) .data(legendData)
.join('g') .join('g')
.attr('transform', (d, i, nodes) => `translate(${config.dendWidth + config.xAxisLength * (config.radius * 2) + 160}, ${config.height / 3 + (nodes.length / 2 - i - 1) * 21 + 110})`) .attr('transform', (d, i, nodes) => `translate(0, ${i * 21 + 10})`)
.call((g) => g.append('rect').attr('width', 15).attr('height', 15).attr('fill', config.color())) .call((g) => g.append('rect').attr('width', 15).attr('height', 15).attr('fill', config.color()))
.call((g) => .call((g) =>
g g
...@@ -331,12 +331,12 @@ export const setConsensusLegend = (config) => { ...@@ -331,12 +331,12 @@ export const setConsensusLegend = (config) => {
// Silhouette legend // Silhouette legend
export const setSilhouetteLegend = (config) => { export const setSilhouetteLegend = (config) => {
const textX = config.dendWidth + config.xAxisLength * (config.radius * 2) + 200
const legendData = [config.maxNumber, config.minNumber] const legendData = [config.maxNumber, config.minNumber]
const textX = config.dendWidth + config.xAxisLength * (config.radius * 2) + 200
let yRatio = 350;
config.svg.append('text') config.svg.append('text')
.attr('x', textX) .attr('x', textX)
.attr('y', config.height / 3 + 210) .attr('y', config.dendWidth * 2 + yRatio)
.attr('fill', '#333333') .attr('fill', '#333333')
.attr('font-size', '20px') .attr('font-size', '20px')
.attr('font-family', `${config.titleConfig.fontFamily}`) .attr('font-family', `${config.titleConfig.fontFamily}`)
...@@ -346,11 +346,12 @@ export const setSilhouetteLegend = (config) => { ...@@ -346,11 +346,12 @@ export const setSilhouetteLegend = (config) => {
.text('silhouette'); .text('silhouette');
config.svg.append('g') config.svg.append('g')
.attr('transform', `translate(${config.dendWidth + config.xAxisLength * (config.radius * 2) + 160}, ${config.margin + config.dendWidth * 2 + yRatio})`)
.selectAll() .selectAll()
.data(legendData) .data(legendData)
.join('text') .join('text')
.attr('x', textX + 20) .attr('x', 0 + 60)
.attr('y', (d, i) => config.height / 3 + i * 40 + 240) .attr('y', (d, i) => i * 40 + 15 )
.attr('fill', config.titleConfig.color) .attr('fill', config.titleConfig.color)
.attr('font-style', `${config.titleConfig.fontStyle}`) .attr('font-style', `${config.titleConfig.fontStyle}`)
.attr('font-weight', `${config.titleConfig.fontWeight}`) .attr('font-weight', `${config.titleConfig.fontWeight}`)
...@@ -383,7 +384,7 @@ export const setSilhouetteLegend = (config) => { ...@@ -383,7 +384,7 @@ export const setSilhouetteLegend = (config) => {
.attr('height', '60') .attr('height', '60')
.attr("fill", "url(#mylLnearGradient)") .attr("fill", "url(#mylLnearGradient)")
.attr("x", `${config.dendWidth + config.xAxisLength * (config.radius * 2) + 160}`) .attr("x", `${config.dendWidth + config.xAxisLength * (config.radius * 2) + 160}`)
.attr("y", `${config.height / 3 + 225}`) .attr("y", `${config.margin + config.dendWidth * 2 + yRatio}`)
} }
// 标签 // 标签
...@@ -391,15 +392,14 @@ export const setAxisTitle = (config) => { ...@@ -391,15 +392,14 @@ export const setAxisTitle = (config) => {
const labelList = config.root.leaves().map((item) => item.data.id); const labelList = config.root.leaves().map((item) => item.data.id);
config.svg.append('g') config.svg.append('g')
.selectAll('g')
.data(labelList)
.join('g')
.attr( .attr(
'transform', 'transform',
(d, i) => `translate(${config.margin + config.dendWidth + config.xAxisLength * 2 * config.radius + 4},${config.margin + config.dendWidth * 2})`
`translate(${config.margin + config.dendWidth + config.xAxisLength * 2 * config.radius + 4},${config.margin + config.dendWidth * 2 + i * 2 * config.radius + 4
})`
) )
.selectAll('g')
.data(labelList)
.join('g')
.attr('transform', (d, i) => `translate(0,${i * 2 * config.radius + 3})`)
.call((g) => .call((g) =>
g g
.append('text') .append('text')
......
<template>
<div ref="chartRef" class="chart-box">
<div class="tip"></div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { chartMockData } from '../mock.js';
import * as d3 from 'https://cdn.jsdelivr.net/npm/d3@7/+esm';
import PubSub from 'pubsub-js';
import {
getRootConfig,
setColor,
setTips,
setMaxMinNumber,
setHeatmap,
setLegend,
setBasisLegend,
setConsensusLegend,
setAxisTitle,
setLabel,
setBasisHeatmap,
setConsensusHeatmap,
setSilhouetteHeatmap,
setSilhouetteLegend
} from '../utils/index';
const chartRef = ref(null);
// 配置项数据
const options = ref({});
// 图表实例
const svgInstance = ref(null);
// pubsub实例
const pubSubInstance = ref(null);
// 生成图表
const generateChart = () => {
const chartData = chartMockData.slice(50).map((item) => ({ ...item, values: item.values.slice(0, 10) }));
const ret = options.value;
let width = Number(ret.chart_width) || 1000;
let height = Number(ret.chart_height) || 1000;
let margin = 50;
let radius = 3;
let dendWidth = 0;
let xAxisLength = chartData[0].values.length;
const heatWidth = 2 * radius * xAxisLength;
const heatHeight = 2 * radius * chartData.length;
const titleConfig = {
label: !!ret.title ? ret.title : null,
color: ret.title_font_color || '#333333',
fontSize: ret.title_font_size || 20,
fontStyle: ret.title_font_style ? 'oblique' : 'normal',
fontFamily: ret.title_font_family || 'Arial',
fontWeight: ret.title_font_weight ? 'bold' : 'normal'
};
const svg = d3
.create('svg')
.attr('width', width)
.attr('height', height + 100)
.attr('viewBox', [0, 0, width, height])
.attr('style', 'max-width: 100%; height: auto; font: 10px sans-serif;');
let configObj = {
svg,
width,
height,
radius,
margin,
dendWidth,
titleConfig,
xAxisLength,
heatWidth,
heatHeight,
...ret
};
const chartData1 = chartMockData.slice(0, 50).map((item) => ({ ...item, values: item.values.slice(0, 10) }));
const chartData2 = chartMockData.slice(50).map((item) => ({ ...item, values: item.values.slice(10, 20) }));
const chartData3 = chartMockData.slice(50).map((item) => ({ ...item, values: item.values.slice(20, 30) }));
const chartData4 = chartMockData.slice(50).map((item) => ({ ...item, values: item.values.slice(30, 40) }));
const chartData5 = chartMockData.slice(50).map((item) => ({ ...item, values: item.values.slice(40, 50) }));
const chartDataArr = [chartData1, chartData2, chartData3, chartData4, chartData5];
for (let i = 0; i < chartDataArr.length; i++) {
setRankChart(configObj, chartDataArr[i], i);
}
svgInstance.value = svg.node();
// Append the SVG element.
chartRef.value.appendChild(svgInstance.value);
};
const setRankChart = (config, chartData, index) => {
const title = `rank = ${index + 2}`;
let xRatio = 350;
let yRatio = 500;
if (index <= 2) {
xRatio = index * xRatio;
yRatio = 0;
} else if (index == 3) {
xRatio = 0;
}
let xAxisLength = chartData[0].values.length;
// 位置
const position = {
title: [config.margin + xRatio, 0 + yRatio],
heatmap: [10 + xRatio, config.margin + yRatio],
legend: [xAxisLength * (config.radius * 2) + 70 + xRatio, config.margin + yRatio],
axisLabel: [xAxisLength * (config.radius * 2) + 12 + xRatio, config.margin + yRatio]
};
const rootConfig = getRootConfig(config, chartData);
// 最大值 最小值
const { maxNumber, minNumber } = setMaxMinNumber(chartData);
// color config
let colors = [
'#313695',
'#436FB1',
'#6BA2CB',
'#9BCCE2',
'#F0F9D8',
'#F0F9D8',
'#FEF0A9',
'#FDCD7E',
'#FA9C58',
'#EE613D',
'#D22B26',
'#A50026'
];
const color = setColor(minNumber, maxNumber, colors);
const configObj = { ...config, ...rootConfig, maxNumber, minNumber, color, title, position };
// heatmap
const heatmapRect = setHeatmap(configObj);
// tip
setTips(heatmapRect);
// axisLabel
setAxisTitle(configObj);
// legend
configObj.show_legend && setLegend(configObj);
// label
setLabel(configObj);
};
const setConsensus = (config) => {
const chartData = chartMockData.slice(0, 2);
// max/min number
const { maxNumber, minNumber } = setMaxMinNumber(chartData);
// root config
const { root, orderingx } = getRootConfig(config, chartData);
// color config
const colors = ['#9D9FFF', '#F57BFF', '#3AB1FF', '#D38BFF', '#00C745', '#F4952C'];
const color = setColor(minNumber, maxNumber, colors);
const configObj = { ...config, root, orderingx, maxNumber, minNumber, color };
const heatmapRect = setBasisHeatmap(configObj);
// tips
setTips(heatmapRect);
// legend
config.show_legend && setBasisLegend(configObj);
};
onMounted(() => {
pubSubInstance.value = PubSub.subscribe('options', (m, data) => {
options.value = data;
chartRef.value.removeChild(svgInstance.value);
generateChart();
});
generateChart();
});
onUnmounted(() => {
PubSub.unsubscribe(pubSubInstance.value);
});
</script>
<style scoped>
.chart-box {
position: relative;
}
</style>
<template>
<div class="form-container">
<h3 class="type content">画布</h3>
<div class="form-item">
<span>宽:</span>
<input type="text" v-model="dynamic_parameter.chart_width" />
</div>
<div class="form-item">
<span>高:</span>
<input type="text" v-model="dynamic_parameter.chart_height" />
</div>
<h3 class="type content">文本</h3>
<div class="form-item">
<span>标题:</span>
<input type="text" v-model="dynamic_parameter.title" />
</div>
<div class="form-item">
<span>字体:</span>
<input type="text" v-model="dynamic_parameter.title_font_family" />
</div>
<div class="form-item">
<span>字号:</span>
<input type="text" v-model="dynamic_parameter.title_font_size" />
</div>
<div class="form-item">
<span>字体颜色:</span>
<input type="text" v-model="dynamic_parameter.title_font_color" />
</div>
<div class="form-item">
<span>字体加粗:</span>
<input type="checkbox" v-model="dynamic_parameter.title_font_weight" />
</div>
<div class="form-item">
<span>斜体:</span>
<input type="checkbox" v-model="dynamic_parameter.title_font_style" />
</div>
<h3 class="type content">图例</h3>
<div class="form-item">
<span>显示图例:</span>
<input type="checkbox" v-model="dynamic_parameter.show_legend" />
</div>
<div class="btn">
<button @click="submit">提交</button>
</div>
</div>
</template>
<script setup>
import { reactive, onMounted } from 'vue';
import PubSub from 'pubsub-js';
//动态图参数,固定参数
const dynamic_parameter = reactive({
//长宽间距
chart_width: 1000,
chart_height: 1000,
margin_top: 60,
margin_right: 60,
margin_bottom: 60,
margin_left: 60,
//标题配置
title: 'Consensus matrix',
title_font_family: 'Arial',
title_font_weight: true,
title_font_size: 20,
title_font_color: '#333333',
title_font_style: false,
//横轴标题
xtitle_font_family: 'Arial',
xtitle_font_weight: false,
xtitle_font_style: false,
xtitle_font_size: 16,
xtitle_font_color: '#333333',
//纵轴标题
ytitle_font_family: 'Arial',
ytitle_font_weight: false,
ytitle_font_style: false,
ytitle_font_size: 16,
ytitle_font_color: '#333333',
//颜色设置
data_color_list: [],
//图例
show_legend: true,
legend_position: '0,0',
//坐标轴
show_x_axis: true,
show_x_line: true,
show_x_tickMarks: true,
show_x_gridLine: false,
show_x_title: true,
x_title: '',
x_tickMarks_color: '#333333',
x_tickMarks_length: '5',
show_y_axis: true,
show_y_line: true,
show_y_tickMarks: true,
show_y_gridLine: false,
show_y_title: true,
y_title: '',
y_tickMarks_color: '#333333',
y_tickMarks_length: '5',
//节点相关的参数,可能还会有更多
node_id: null, //节点id
node_color: null, //节点颜色
is_delect_node: false, //是否删除节点
node_search_name: null, //节点名称搜索
node_search_color: '#FF0404', //节点搜索后的颜色
node_edit_name: null, //修改节点名称
node_height: 100, //节点大小
path_id: null
});
const submit = () => {
PubSub.publish('options', dynamic_parameter);
};
onMounted(() => {
submit();
});
</script>
<style scoped>
.form-item span {
display: inline-block;
width: 100px;
text-align: left;
}
.btn {
width: 100%;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
}
</style>
<template>
<div class="chart-container">
<Chart />
<Config />
</div>
</template>
<script setup>
import Chart from './components/chart.vue';
import Config from './components/config.vue';
</script>
<style scoped>
.chart-container {
display: flex;
}
</style>
This source diff could not be displayed because it is too large. You can view the blob instead.
差异被折叠。
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论