From 4093e6b05674b7dea0910395f2acb23c29afd837 Mon Sep 17 00:00:00 2001 From: reng Date: Thu, 8 Jan 2026 15:37:16 +0800 Subject: [PATCH] update --- v2/app/src/components/graph.jsx | 2 +- v2/app/src/components/point.jsx | 91 ++++++++++++++++++--------------- v2/app/src/utils/draw.js | 4 +- v2/app/src/utils/parsing.js | 27 +++++++++- v2/scrapper/main.js | 2 +- 5 files changed, 79 insertions(+), 47 deletions(-) diff --git a/v2/app/src/components/graph.jsx b/v2/app/src/components/graph.jsx index ce39d5b..fc03f7b 100644 --- a/v2/app/src/components/graph.jsx +++ b/v2/app/src/components/graph.jsx @@ -68,7 +68,7 @@ export default function Graph({results}){ console.log("3D Points:", newPoints); setPoints(newPoints); - generateLinesFromPoints(newPoints).then(newLines=>{ + generateLinesFromPoints(newPoints, 10).then(newLines=>{ console.log("Lines:", newLines); setLines(newLines); }); diff --git a/v2/app/src/components/point.jsx b/v2/app/src/components/point.jsx index c3351c2..91ab9b8 100644 --- a/v2/app/src/components/point.jsx +++ b/v2/app/src/components/point.jsx @@ -3,6 +3,7 @@ import { useEffect, useRef, useState } from 'react'; import * as THREE from 'three'; import { useFrame, useThree } from '@react-three/fiber'; import gsap from 'gsap'; +import { normalize } from 'umap-js/dist/matrix'; export const PointScale=200; @@ -10,11 +11,12 @@ const PointSize=1; const PointDuration=3; const KeywordOffset=0; +const SceneDuration=10; // 總場景時間,用於計算延遲 const DEPTH_CONFIG = { - NEAR: 600, // 在此距離內顯示最清晰 - FAR: 1000, // 超過此距離後幾乎隱藏或變成符號 - FADE_START: 400 + NEAR: 1200, // 在此距離內顯示最清晰 + FAR: 1500, // 超過此距離後幾乎隱藏或變成符號 + FADE_START: 800 }; @@ -35,13 +37,14 @@ export default function Point({point, index, totalPoints, result, showContent, s const meshRef=useRef(); const refText=useRef(); const { camera } = useThree(); + const refAnimation=useRef({t:0}); function getRandomText(text){ if(contentType==PointContentType.Blur) return text.slice(0, textToShow) + '...'; const length=text.length; - const characters='░▒▓█'; + const characters='*@#$%^&'; let result=''; for(let i=0; i{ - return; + // return; if(!payload) return; - const lifeTime=payload.number/payload.total; - console.log("Point lifeTime:", lifeTime); + const lifeTime=payload.normalized_time || 0; + // console.log("Point lifeTime:", lifeTime); // animate point size based on lifeTime const targetSize=1; - const initialSize=targetSize; const timeline=gsap.timeline({ repeat:-1}); - timeline.set(meshRef.current.scale, { - x: initialSize, y: initialSize, z: initialSize, - }); - timeline.set(refText.current, { - scale: initialSize, - opacity: 0, - }); - timeline.fromTo(meshRef.current.scale, { - x: initialSize, y: initialSize, z: initialSize, - }, { - x: PointSize, y: PointSize, z: PointSize, - duration:PointDuration, - delay: lifeTime *PointDuration, - ease: "power3.in", - onUpdate:()=>{ - if(refText.current){ - refText.current.style.transform=`scale(${meshRef.current.scale.x / PointSize})`; - refText.current.style.opacity=`${meshRef.current.scale.x / PointSize}`; - } + timeline.fromTo(refAnimation.current,{t:0}, { + t:0, + onUpdate: ()=>{ + setTextStyle(refAnimation.current.t); } - }); - timeline.to(meshRef.current.scale, { - x: targetSize, y: targetSize, z: targetSize, - duration:PointDuration, - ease: "power3.out", - onUpdate:()=>{ - if(refText.current){ - refText.current.style.transform=`scale(${meshRef.current.scale.x / PointSize})`; - refText.current.style.opacity=`${meshRef.current.scale.x / PointSize}`; - } + }, lifeTime * SceneDuration); + timeline.to(refAnimation.current,{ + t: 1, + duration: PointDuration, + ease: 'power1.inOut', + onComplete: ()=>{ + console.log("Show text for point:", index); + }, + onUpdate: ()=>{ + setTextStyle(refAnimation.current.t); + } + }); // 根據 lifeTime 設定延遲時間 + timeline.to(refAnimation.current,{ + t: 0, + duration: PointDuration, + onComplete: ()=>{ + console.log("Hide text for point:", index); + }, + onUpdate: ()=>{ + setTextStyle(refAnimation.current.t); } - }); + }); + // timeline.to(refAnimation.current,{t:0}, { + // duration: SceneDuration - (lifeTime * SceneDuration + PointDuration * 2), + // }); // 剩餘時間保持隱藏 timeline.play(); @@ -177,6 +183,7 @@ export default function Point({point, index, totalPoints, result, showContent, s total: result.payload.total || '1', content: JSON.parse(result.payload.metadata).text || '', teaser: result.payload.teaser || '', + normalized_time: result.payload.normalized_time || 0, }); },[point]); @@ -191,13 +198,13 @@ export default function Point({point, index, totalPoints, result, showContent, s emissiveIntensity={0.5} /> -
+
{showKeyword &&
{payload?.keywords.map((el, index)=>{el})}
} {showContent &&
}
diff --git a/v2/app/src/utils/draw.js b/v2/app/src/utils/draw.js index 8877fcf..1ff5385 100644 --- a/v2/app/src/utils/draw.js +++ b/v2/app/src/utils/draw.js @@ -18,7 +18,7 @@ export async function get3dCoordinates(rawPoints) { } -export async function generateLinesFromPoints(points){ +export async function generateLinesFromPoints(points, maxDistance=300) { if(!Array.isArray(points) || points.length<2){ return []; } @@ -34,7 +34,7 @@ export async function generateLinesFromPoints(points){ Math.pow(end[1]-start[1], 2) + Math.pow(end[2]-start[2], 2) ); - if(distance > 20) continue; // skip if too far apart + if(distance > maxDistance) continue; // skip if too far apart lines.push([start.map(coord => coord * PointScale), end.map(coord => coord * PointScale)]); } diff --git a/v2/app/src/utils/parsing.js b/v2/app/src/utils/parsing.js index 4dc0291..3bc6bc8 100644 --- a/v2/app/src/utils/parsing.js +++ b/v2/app/src/utils/parsing.js @@ -130,7 +130,10 @@ export async function searchByTheme(themeId, limit){ const data = await res.json(); console.log(data); - return await searchQdrant(data.result.vector, limit); + const allResults=await searchQdrant(data.result.vector, limit); + normalizeResultTime(allResults); + + return allResults; } export async function searchByKeywords(keywordIds, limit){ @@ -155,6 +158,8 @@ export async function searchByKeywords(keywordIds, limit){ allResults.push(...results); } + + normalizeResultTime(allResults); return allResults; } @@ -185,3 +190,23 @@ async function searchQdrant(query_embeddings, limit=10){ return data.result; } + +function normalizeResultTime(results){ + + let min, max; + results.forEach(item=>{ + const time = JSON.parse(item.payload.metadata).published_on; + if(!min || timemax) max=time; + }); + + console.log("Time range:", min, max); + + results.forEach(item=>{ + const time = JSON.parse(item.payload.metadata).published_on; + item.payload.normalized_time = (time - min) / (max - min); + }); + + return results; + +} \ No newline at end of file diff --git a/v2/scrapper/main.js b/v2/scrapper/main.js index 28e0e92..3e2eac6 100644 --- a/v2/scrapper/main.js +++ b/v2/scrapper/main.js @@ -53,7 +53,7 @@ const Keywords=[ const Version="v2-1"; const DEBUG_MODE=false; -const SCRAP_TYPE='TAG'; // 'KEYWORD' or 'TAG' +const SCRAP_TYPE='KEYWORD'; // 'KEYWORD' or 'TAG' async function step1(){ // const chooseKeywords=['人生 妥協','邊界 被侵犯','卡住 職涯','尷尬 年齡','厭世 創作'];