From 81ae2c7e29307457880d37ded5785e971c850b4d Mon Sep 17 00:00:00 2001 From: reng Date: Wed, 14 Jan 2026 16:33:53 +0800 Subject: [PATCH] add group color --- v2/app/src/App.css | 2 +- v2/app/src/App.jsx | 2 +- v2/app/src/components/graph.jsx | 51 +++++++++++++++++++++++++++++++-- v2/app/src/components/point.jsx | 24 +++++++++------- v2/app/src/utils/draw.js | 26 ++++++++++------- v2/app/src/utils/osc.js | 2 +- v2/app/src/utils/parsing.js | 15 ++++++++-- 7 files changed, 93 insertions(+), 29 deletions(-) diff --git a/v2/app/src/App.css b/v2/app/src/App.css index 52cedfb..d98a466 100644 --- a/v2/app/src/App.css +++ b/v2/app/src/App.css @@ -17,5 +17,5 @@ form{ /* @apply text-sm opacity-50; */ } .keyword > span { - @apply bg-gray-200 text-black rounded-full p-2; + @apply bg-gray-800 rounded-full p-2; } \ No newline at end of file diff --git a/v2/app/src/App.jsx b/v2/app/src/App.jsx index bc183e9..7bcada0 100644 --- a/v2/app/src/App.jsx +++ b/v2/app/src/App.jsx @@ -14,7 +14,7 @@ function App() { const [themes, setThemes]=useState([]); const [keywords, setKeywords]=useState([]); const [results, setResults]=useState([]); - const [selectTab, setSelectTab]=useState('text'); + const [selectTab, setSelectTab]=useState(DisplayTypes[1]); function preText(){ diff --git a/v2/app/src/components/graph.jsx b/v2/app/src/components/graph.jsx index f55e643..b2b1423 100644 --- a/v2/app/src/components/graph.jsx +++ b/v2/app/src/components/graph.jsx @@ -7,21 +7,30 @@ import Point, {PointContentType} from './point.jsx'; import { useControls, button } from "leva"; import { sendOsc } from '../utils/osc.js'; +function getColorFromIndex(index, length){ + const hue = (index / length) * 360; + return `hsl(${hue}, 100%, 50%)`; +} + export default function Graph({results}){ const [points, setPoints]=useState(); const [lines, setLines]=useState(); + // const [drawLines, setDrawLines]=useState(false); // const [showContent, setShowContent]=useState(true); // const [showKeyword, setShowKeyword]=useState(false); - const { showContent, contentType, showKeywords, showLines, textToShow } = useControls({ + const { showContent, contentType, showKeywords, showLines, textToShow, drawDistance, distToKeywords, keywordColor } = useControls({ showContent: { value: true }, contentType: { options: Object.values(PointContentType), value: PointContentType.Teaser }, showKeywords: { value: false }, showLines: { value: false }, textToShow: { value: 20, min: 5, max: 100, step: 5 }, + drawDistance: { value: 0.2, min: 0.1, max: 5, step: 0.01 }, + keywordColor: { value: true }, + distToKeywords: { value: 3, min: 0.1, max: 10, step: 0.01 }, }); @@ -35,13 +44,36 @@ export default function Graph({results}){ bounds?.refresh().clip().fit(); } + function getColorByGroup(result){ + + if(!keywordColor) return 'white'; + + if(!result || !result.payload ) + return 'white'; + + + if(result.type==='keyword'){ + + return getColorFromIndex(parseInt(result.id), 20); + } + + const group = result.payload.group[0]; + if(group.distance{ zoomToFit(); if(!points || points.length===0) return; points.forEach((point, index)=>{ - console.log(`send osc Point ${index}: (${point[0].toFixed(2)}, ${point[1].toFixed(2)}, ${point[2].toFixed(2)})`); + // console.log(`send osc Point ${index}: (${point[0].toFixed(2)}, ${point[1].toFixed(2)}, ${point[2].toFixed(2)})`); sendOsc('/point',JSON.stringify({ index: index, x: point[0], @@ -52,6 +84,18 @@ export default function Graph({results}){ }); }, [points]); + useEffect(()=>{ + if(!results || results.length===0){ + setLines([]); + return; + } + + generateLinesFromPoints(points, drawDistance).then(newLines=>{ + console.log("Lines updated due to drawDistance change:", newLines); + setLines(newLines); + }); + + },[drawDistance]); useEffect(()=>{ if(!results || results.length===0){ @@ -68,7 +112,7 @@ export default function Graph({results}){ console.log("3D Points:", newPoints); setPoints(newPoints); - generateLinesFromPoints(newPoints, 10).then(newLines=>{ + generateLinesFromPoints(newPoints, drawDistance).then(newLines=>{ console.log("Lines:", newLines); setLines(newLines); }); @@ -94,6 +138,7 @@ export default function Graph({results}){ result={results?.[index]} showContent={showContent} showKeyword={showKeywords} contentType={contentType} textToShow={textToShow} + color={getColorByGroup(results?.[index])} /> ))} diff --git a/v2/app/src/components/point.jsx b/v2/app/src/components/point.jsx index bc00d82..9cf4d95 100644 --- a/v2/app/src/components/point.jsx +++ b/v2/app/src/components/point.jsx @@ -31,7 +31,7 @@ export const PointContentType={ } -export default function Point({point, index, totalPoints, result, showContent, showKeyword, contentType, textToShow}) { +export default function Point({point, index, totalPoints, result, showContent, showKeyword, contentType, textToShow, color}) { const [payload, setPayload]=useState(); const [showPayload, setShowPayload]=useState(false); @@ -118,16 +118,17 @@ export default function Point({point, index, totalPoints, result, showContent, s const finalHtmlScale = depthScaleFactor * (currentMeshScale / PointSize); refText.current.style.transform = `translate(-50%, -50%) scale(${finalHtmlScale})`; - if(payload.normalized_time===-1){ + if(payload.keywords.length==1){ // meshRef.current.scale.set(PointSize, PointSize, PointSize); refText.current.style.opacity = 1; refText.current.style.transform = `translate(-50%, -50%) scale(1)`; + refText.current.style.filter = `none`; }else{ refText.current.style.opacity *= alpha; // 加入 CSS 濾鏡:越遠越模糊 const blurAmount = distance > DEPTH_CONFIG.FADE_START ? (distance - DEPTH_CONFIG.FADE_START) / 20 : 0; - refText.current.style.filter = `blur(${Math.min(blurAmount, 4)}px)`; + refText.current.style.filter = `blur(${Math.min(blurAmount, 2)}px)`; } // 效能優化 // refText.current.style.display = alpha <= 0.01 ? 'none' : 'block'; @@ -263,18 +264,21 @@ export default function Point({point, index, totalPoints, result, showContent, s -
- {payload?.keywords &&
- {payload?.keywords.map((el, index)=>{el})} +
+ {showKeyword && payload?.keywords.length>1 &&
+ {payload?.keywords.map((el, index)=>{el})}
} - {
1 &&
} */} + {
}
diff --git a/v2/app/src/utils/draw.js b/v2/app/src/utils/draw.js index 1ff5385..5a39e0f 100644 --- a/v2/app/src/utils/draw.js +++ b/v2/app/src/utils/draw.js @@ -26,17 +26,21 @@ export async function generateLinesFromPoints(points, maxDistance=300) { const lines=[]; for(let i=0; i maxDistance) continue; // skip if too far apart - - lines.push([start.map(coord => coord * PointScale), end.map(coord => coord * PointScale)]); + for(let j=i+1; j maxDistance) continue; // skip if too far apart + + lines.push([start.map(coord => coord * PointScale), end.map(coord => coord * PointScale)]); + } } return lines; diff --git a/v2/app/src/utils/osc.js b/v2/app/src/utils/osc.js index e5e4bd6..7c970d9 100644 --- a/v2/app/src/utils/osc.js +++ b/v2/app/src/utils/osc.js @@ -3,6 +3,6 @@ import { invoke } from "@tauri-apps/api/core"; const TD_OSC_PORT=7000; export function sendOsc(key, message){ - console.log("Sending OSC:", key, message); + // console.log("Sending OSC:", key, message); invoke('send_osc_message', {key: `${key}`, message: message, host:'0.0.0.0:0', target:`127.0.0.1:${TD_OSC_PORT}`}) } \ No newline at end of file diff --git a/v2/app/src/utils/parsing.js b/v2/app/src/utils/parsing.js index 0bc100f..626a107 100644 --- a/v2/app/src/utils/parsing.js +++ b/v2/app/src/utils/parsing.js @@ -141,6 +141,7 @@ export async function searchByKeywords(keywordIds, limit){ // get keyword embeddings from Qdrant let allResults = []; let keywords=[]; + for(const keywordId of keywordIds){ const res=await fetch(`http://localhost:6333/collections/${COLLECTION_KEYWORD}/points/${parseInt(keywordId)-1}`, { method: 'GET', @@ -157,9 +158,9 @@ export async function searchByKeywords(keywordIds, limit){ const data = await res.json(); console.log(data); - const results = await searchQdrant(data.result.vector, Math.floor(limit/keywordIds.length)); + // const results = await searchQdrant(data.result.vector, Math.floor(limit/keywordIds.length)); - allResults.push(...results); + // allResults.push(...results); const keypoint={ type: 'keyword', @@ -170,6 +171,16 @@ export async function searchByKeywords(keywordIds, limit){ keywords.push(keypoint); allResults.push(keypoint); } + // search by centroid of keywords + const centroid = new Array(keywords[0].vector.length).fill(0); + keywords.forEach(kw=>{ + kw.vector.forEach((val, index)=>{ + centroid[index] += val/keywords.length; + }); + }); + + const results = await searchQdrant(centroid, limit); + allResults.push(...results); normalizeResultTime(allResults);