Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add draw background for sequence-scatter #3787

Open
wants to merge 2 commits into
base: feat/sequence-scatter
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions VChart
Submodule VChart added at 857dcb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,20 @@ import { registerSequenceScatter } from '../../../../src';
import { VChart } from '@visactor/vchart';
import trainingData1 from '../data/sequence-scatter/Training_process1/data.json';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import trainingData2 from '../data/sequence-scatter/Training_process2/data.json';
// import trainingData2 from '../data/sequence-scatter/Training_process2/data.json';
import bgimgData from '../data/sequence-scatter/Training_process1/bgimg_data.json';
const origianlData = trainingData1;
// const origianlData = trainingData2;
const bgData = {};
if (bgimgData) {
// 假设每个帧对应一个300x300的RGB矩阵
Object.keys(bgimgData).forEach(inter => {
// 确保原始数据中有这一帧
if (origianlData[inter]) {
bgData[inter] = bgimgData[inter];
}
});
}
const chartData = {};
Object.keys(origianlData).forEach(inter => {
chartData[inter] = [];
Expand All @@ -21,6 +32,8 @@ const spec = {
xField: 'x',
yField: 'y',

backgroundColors: bgData,

infoLabel: {
visible: true,
style: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const DATA_KEY = 'dataKey';
export const BACKGROUND_KEY = 'background';
14 changes: 14 additions & 0 deletions packages/vchart-extension/src/charts/sequence-scatter/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,18 @@ export interface ISequenceScatterSpec {
visible: boolean;
style: ITextGraphicAttribute;
};
/**
* 背景数据
*/
backgroundColors: {
[Iteration: string]: any;
};
/**
* 宽度
*/
width: number;
/**
* 高度
*/
height: number;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Datum } from '@visactor/vchart/src/typings';
import type { ISequenceScatterSpec } from './interface';
import { CommonChartSpecTransformer } from '@visactor/vchart';

const DATA_KEY = 'dataKey';
import { processSequenceData } from '../../utils/processSequenceData';
import { DATA_KEY } from './constant';

export class SequenceScatterChartSpecTransformer extends CommonChartSpecTransformer<any> {
transformSpec(spec: any): void {
Expand All @@ -12,6 +12,11 @@ export class SequenceScatterChartSpecTransformer extends CommonChartSpecTransfor
spec.type = 'common';
spec.dataKey = DATA_KEY;
spec.data = dataSpecs[0].data;

spec.width = 300;
spec.height = 300;
spec.autoFit = false;

spec.series = [
{
type: 'scatter',
Expand Down Expand Up @@ -49,6 +54,19 @@ export class SequenceScatterChartSpecTransformer extends CommonChartSpecTransfor
];

spec.customMark = [
// 背景图像
{
type: 'image',
dataIndex: 2,
style: {
x: 0,
y: 0,
width: 300,
height: 300,
image: (datum: Datum) => datum.imageData,
zIndex: -1
}
},
{
type: 'text',
dataIndex: 1,
Expand All @@ -65,22 +83,6 @@ export class SequenceScatterChartSpecTransformer extends CommonChartSpecTransfor
...spec.infoLabel?.style
}
}
// TODO: draw polygon according to data
// {
// type: 'polygon',
// dataIndex: 1,
// style: {
// points: (datum: Datum) => {
// return [
// {
// x: ,
// y:
// },
// //....
// ];
// },
// }
// }
];

spec.tooltip = {
Expand All @@ -90,33 +92,3 @@ export class SequenceScatterChartSpecTransformer extends CommonChartSpecTransfor
super.transformSpec(spec);
}
}

export function processSequenceData(spec: ISequenceScatterSpec) {
const result: any[] = [];
Object.keys(spec.data).forEach(inter => {
result.push({
data: [
{
id: 'nodes',
values: spec.data[inter].map((d, i) => {
return { ...d, [DATA_KEY]: i };
})
},
// TODO: edges data
// {
// id: 'edges',
// values: [....]
// },
{
id: 'inter',
values: [
{
inter
}
]
}
]
});
});
return result;
}
40 changes: 40 additions & 0 deletions packages/vchart-extension/src/utils/createImageData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ISequenceScatterSpec } from '../charts/sequence-scatter/interface';

// 将RGB三元组数组转换为Canvas可用的imageData
export function createImageDataFromColorMatrix(colorMatrix: any[][], spec: ISequenceScatterSpec): string | null {
// 浏览器环境检查
if (typeof document === 'undefined') {
throw new Error('Canvas rendering requires browser environment with document object'); // 非浏览器环境下返回null
}

// 创建Canvas离屏渲染
const canvas = document.createElement('canvas');
canvas.width = spec.width;
canvas.height = spec.height;
const ctx = canvas.getContext('2d');

if (!ctx) {
throw new Error('Failed to get 2D rendering context from canvas'); // Canvas context creation failed
}

// 创建imageData
const imageData = ctx.createImageData(spec.width, spec.height);

// 填充像素数据
for (let y = 0; y < 300; y++) {
for (let x = 0; x < 300; x++) {
const rgb = colorMatrix[y]?.[x] || [0, 0, 0];
const pixelIndex = (y * 300 + x) * 4;

// 转换0-1范围到0-255
imageData.data[pixelIndex] = Math.round(rgb[0] * 255); // R
imageData.data[pixelIndex + 1] = Math.round(rgb[1] * 255); // G
imageData.data[pixelIndex + 2] = Math.round(rgb[2] * 255); // B
imageData.data[pixelIndex + 3] = 255; // A (完全不透明)
}
}

// 将imageData绘制到canvas然后返回dataURL
ctx.putImageData(imageData, 0, 0);
return canvas.toDataURL('image/png');
}
37 changes: 37 additions & 0 deletions packages/vchart-extension/src/utils/processSequenceData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { BACKGROUND_KEY, DATA_KEY } from '../charts/sequence-scatter/constant';
import { ISequenceScatterSpec } from '../charts/sequence-scatter/interface';
import { createImageDataFromColorMatrix } from './createImageData';

export function processSequenceData(spec: ISequenceScatterSpec) {
const result: any[] = [];
Object.keys(spec.data).forEach(inter => {
let backgroundData = null;
if (spec.backgroundColors && spec.backgroundColors[inter]) {
const imageData = createImageDataFromColorMatrix(spec.backgroundColors[inter], spec);
backgroundData = { imageData };
}
result.push({
data: [
{
id: 'nodes',
values: spec.data[inter].map((d, i) => {
return { ...d, [DATA_KEY]: i };
})
},
{
id: 'inter',
values: [
{
inter
}
]
},
{
id: BACKGROUND_KEY,
values: backgroundData ? [backgroundData] : []
}
]
});
});
return result;
}