From c813344c7c2aeeef40e2c61c91319c1fd664c183 Mon Sep 17 00:00:00 2001 From: jialin Date: Tue, 13 Jan 2026 20:57:02 +0800 Subject: [PATCH] fix: the max embedding value exceeds the limit. --- src/components/echarts/scatter.tsx | 37 ++++++++++---- src/global.less | 7 +-- src/locales/en-US/playground.ts | 3 +- src/locales/ja-JP/playground.ts | 6 ++- src/locales/ru-RU/playground.ts | 6 ++- src/locales/zh-CN/playground.ts | 3 +- .../components/ground-embedding.tsx | 1 + .../config/embedding-worker.worker.ts | 48 ++++++++++++++++++- .../playground/config/video-parameters.ts | 7 +-- 9 files changed, 89 insertions(+), 29 deletions(-) diff --git a/src/components/echarts/scatter.tsx b/src/components/echarts/scatter.tsx index 2ab6c517..8d858313 100644 --- a/src/components/echarts/scatter.tsx +++ b/src/components/echarts/scatter.tsx @@ -2,12 +2,26 @@ import Chart from '@/components/echarts/chart'; import useChartConfig from '@/components/echarts/config'; import EmptyData from '@/components/empty-data'; import _ from 'lodash'; -import React, { memo, useCallback, useMemo, useRef } from 'react'; +import React, { useCallback, useMemo, useRef } from 'react'; import { ChartProps } from './types'; -const Scatter: React.FC = (props) => { +const Scatter: React.FC< + ChartProps & { + xMax?: number; + yMax?: number; + } +> = (props) => { const { grid, title: titleConfig, isDark, chartColorMap } = useChartConfig(); - const { seriesData, xAxisData, height, width, showEmpty, title } = props; + const { + seriesData, + xAxisData, + height, + width, + showEmpty, + title, + xMax = 1, + yMax = 1 + } = props; const chart = useRef(null); @@ -36,8 +50,8 @@ const Scatter: React.FC = (props) => { borderRadius: 4 }, xAxis: { - min: -1, - max: 1, + min: -xMax, + max: xMax, scale: false, slient: true, splitNumber: 15, @@ -62,8 +76,8 @@ const Scatter: React.FC = (props) => { boundaryGap: [0.05, 0.05] }, yAxis: { - min: -1, - max: 1, + min: -yMax, + max: yMax, scale: false, slient: true, splitNumber: 10, @@ -99,7 +113,7 @@ const Scatter: React.FC = (props) => { }, series: [] }; - }, [isDark]); + }, [isDark, xMax, yMax]); const findOverlappingPoints = useCallback( (data: any[], currentPoint: any) => { @@ -201,7 +215,10 @@ const Scatter: React.FC = (props) => { return ( <> {!seriesData.length && showEmpty ? ( - + ) : ( = (props) => { ); }; -export default memo(Scatter); +export default Scatter; diff --git a/src/global.less b/src/global.less index cb46db4b..62309c96 100644 --- a/src/global.less +++ b/src/global.less @@ -212,16 +212,11 @@ body { // set for the non-form item .ant-input-outlined, .ant-input-affix-wrapper, - .ant-select, + .ant-select-lg, .ant-picker { height: 40px; } - // form item help - .ant-form-item-with-help .ant-form-item-explain { - // padding-left: 16px; - } - // icon .anticon { &.size-16 { diff --git a/src/locales/en-US/playground.ts b/src/locales/en-US/playground.ts index 752dd7f4..83f14dab 100644 --- a/src/locales/en-US/playground.ts +++ b/src/locales/en-US/playground.ts @@ -162,5 +162,6 @@ export default { 'Please enter a direct image URL (e.g. https://.../image.png). Press ESC to cancel.', 'playground.uploadImage.url.holder': 'Enter an image URL', 'playground.uploadImage.url.button': 'Add Image from URL', - 'playground.params.duration': 'Duration (seconds)' + 'playground.params.duration': 'Duration (seconds)', + 'playground.params.resolution': 'Resolution' }; diff --git a/src/locales/ja-JP/playground.ts b/src/locales/ja-JP/playground.ts index dfad350f..30cc4d44 100644 --- a/src/locales/ja-JP/playground.ts +++ b/src/locales/ja-JP/playground.ts @@ -165,7 +165,8 @@ export default { 'Please enter a direct image URL (e.g. https://.../image.png). Press ESC to cancel.', 'playground.uploadImage.url.holder': 'Enter an image URL', 'playground.uploadImage.url.button': 'Add Image from URL', - 'playground.params.duration': 'Duration (seconds)' + 'playground.params.duration': 'Duration (seconds)', + 'playground.params.resolution': 'Resolution' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== @@ -175,5 +176,6 @@ export default { // 4. 'playground.uploadImage.url.invalid': 'Please enter a direct image URL(e.g. https://.../image.png). Press ESC to cancel.', // 5. 'playground.uploadImage.url.holder': 'Enter an image URL' // 6. 'playground.uploadImage.url.button': 'Add Image from URL', -// 7. 'playground.params.duration': 'Duration (seconds)' +// 7. 'playground.params.duration': 'Duration (seconds)', +// 8. 'playground.params.resolution': 'Resolution' // ========== End of To-Do List ========== diff --git a/src/locales/ru-RU/playground.ts b/src/locales/ru-RU/playground.ts index 2a4dfbde..86e4a81b 100644 --- a/src/locales/ru-RU/playground.ts +++ b/src/locales/ru-RU/playground.ts @@ -159,12 +159,14 @@ export default { 'Please enter a direct image URL (e.g. https://.../image.png). Press ESC to cancel.', 'playground.uploadImage.url.holder': 'Enter an image URL', 'playground.uploadImage.url.button': 'Add Image from URL', - 'playground.params.duration': 'Duration (seconds)' + 'playground.params.duration': 'Duration (seconds)', + 'playground.params.resolution': 'Resolution' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== // 1. 'playground.uploadImage.url.invalid': 'Please enter a direct image URL(e.g. https://.../image.png). Press ESC to cancel.', // 2. 'playground.uploadImage.url.holder': 'Enter an image URL', // 3. 'playground.uploadImage.url.button': 'Add Image from URL', -// 4. 'playground.params.duration': 'Duration (seconds)' +// 4. 'playground.params.duration': 'Duration (seconds)', +// 5. 'playground.params.resolution': 'Resolution' // ========== End of To-Do List ========== diff --git a/src/locales/zh-CN/playground.ts b/src/locales/zh-CN/playground.ts index 0588da49..fc791ed3 100644 --- a/src/locales/zh-CN/playground.ts +++ b/src/locales/zh-CN/playground.ts @@ -155,5 +155,6 @@ export default { '请输入直接的图片链接(例如:https://…/image.png)。按 ESC 可取消。', 'playground.uploadImage.url.holder': '请输入图片链接', 'playground.uploadImage.url.button': '从链接添加图片', - 'playground.params.duration': '时长 (秒)' + 'playground.params.duration': '时长 (秒)', + 'playground.params.resolution': '分辨率' }; diff --git a/src/pages/playground/components/ground-embedding.tsx b/src/pages/playground/components/ground-embedding.tsx index 39e425cd..9ecce67c 100644 --- a/src/pages/playground/components/ground-embedding.tsx +++ b/src/pages/playground/components/ground-embedding.tsx @@ -215,6 +215,7 @@ const GroundEmbedding: React.FC = forwardRef((props, ref) => { workerRef.current!.onmessage = (event: MessageEvent) => { const { scatterData, embeddingData } = event.data; + setScatterData(scatterData); setEmbeddingData(embeddingData); setLoading(false); diff --git a/src/pages/playground/config/embedding-worker.worker.ts b/src/pages/playground/config/embedding-worker.worker.ts index ec7076a4..2135e35d 100644 --- a/src/pages/playground/config/embedding-worker.worker.ts +++ b/src/pages/playground/config/embedding-worker.worker.ts @@ -1,6 +1,52 @@ import _ from 'lodash'; import { PCA } from 'ml-pca'; +type Point = { + value: [number, number]; + [key: string]: any; +}; + +const normalizeEmbeddingToCanvas = ( + data: Point[], + padding = 0.9 // 90% fill the canvas +) => { + let minX = Infinity, + maxX = -Infinity; + let minY = Infinity, + maxY = -Infinity; + + data.forEach((p) => { + const [x, y] = p.value; + minX = Math.min(minX, x); + maxX = Math.max(maxX, x); + minY = Math.min(minY, y); + maxY = Math.max(maxY, y); + }); + + // 2. center point + const cx = (minX + maxX) / 2; + const cy = (minY + maxY) / 2; + + // 3. max radius + let maxRadius = 0; + data.forEach((p) => { + const dx = Math.abs(p.value[0] - cx); + const dy = Math.abs(p.value[1] - cy); + maxRadius = Math.max(maxRadius, dx, dy); + }); + + if (maxRadius === 0) maxRadius = 1; + + // 4. scale factor + const scale = padding / maxRadius; + + // 5. normalization + return data.map((p) => ({ + ...p, + value: [(p.value[0] - cx) * scale, (p.value[1] - cy) * scale] + })); +}; + self.onmessage = ( event: MessageEvent<{ embeddings: any[]; @@ -49,7 +95,7 @@ self.onmessage = ( }; self.postMessage({ - scatterData: list, + scatterData: normalizeEmbeddingToCanvas(list), embeddingData: embeddingData }); } catch (e) { diff --git a/src/pages/playground/config/video-parameters.ts b/src/pages/playground/config/video-parameters.ts index f8a5ba65..b4424dfd 100644 --- a/src/pages/playground/config/video-parameters.ts +++ b/src/pages/playground/config/video-parameters.ts @@ -36,13 +36,8 @@ export const videoParamsConfig: ParamsSchema[] = [ type: 'Select', name: 'size', options: videoSizeOptions, - description: { - text: 'playground.params.size.description', - html: true, - isLocalized: true - }, label: { - text: 'playground.params.size', + text: 'playground.params.resolution', isLocalized: true }, rules: [